SOLID Principles in iOS Development

SOLID Principles in iOS Development

😩 Ok, you probably already hear this term, but in practice, what’s mean? When uses? How uses? Keep reading for FINALLY LEARN

First of all, SOLID is a s a mnemonic acronym for five des…


This content originally appeared on DEV Community and was authored by MKilmer

SOLID Principles in iOS Development

😩 Ok, you probably already hear this term, but in practice, what's mean? When uses? How uses? Keep reading for FINALLY LEARN

Alt Text

First of all, SOLID is a s a mnemonic acronym for five design principles intended to make software design more understandable, flexible, and maintainable

In all tutorial, i will show SOLID Principles in action and codes that don't follow SOLID

So, let's go 😁

S - Single Responsibility Principle

A classe should have one reason to change ( to exists )

Tips to apply

  • Ask yourself for every entity ( classes / functions ) : this entity, does more than one thing?

  • You shouldn't have use the word and when have to talk the responsibility to entity

💢 NOT Single Responsibility Principle apply

class Handler_NOT_SOLID {

    func handle() { 

        let data = requestDataToAPI()
        guard let dataReceive = data else { return }
        let array = parse(data: dataReceive)
        saveToDB(array: array)

    }

    private func requestDataToAPI() -> Data?{
        // send API request and wait the response
        return nil
    }

    private func parse(data:Data)->[String]?{
        // parse the data and create an array
        return nil
    }

    private func saveToDB(array:[String]?){
        // save array in a database
    }
}

Can you see?

Handler_NOT_SOLID class have several responsibilities

  1. Send request to API
  2. Create an array with data receive
  3. Save array in a database

👍 Single Responsibility Principle apply

class Handler_SOLID {

    let apiHandler: APIHandler
    let parseHandler: ParseHandler
    let dbHandler: DBHandler

    init(apiHandler: APIHandler, parseHandler: ParseHandler, dbHandler: DBHandler) {
        self.apiHandler = apiHandler
        self.parseHandler = parseHandler
        self.dbHandler = dbHandler
    }
}


class APIHandler {

    func requestDataToAPI() -> Data?{
        // send API request and wait the response
        return nil
    }
}

class ParseHandler {
    func parse(data:Data) -> [String]?{
        // parse the data and create an array
        return nil
    }
}

class DBHandler {
    func saveToDB(array:[String]?){
        // save array in a database
    }
}

Now, each entity have just one responsibility

O - Open/Closed Principle

A software entity should be open to extension, but closed for modification.

Tips to apply

  1. If you want to modify a class every time a new behavior is added, something isn't quite right
  2. If/else/switch statements don't be used to modify a behavior

💢 NOT Open/Closed Principle apply

class Vehicles_NOT_SOLID {

    func printData() {
        let cars = [
            Car_NOT_SOLID(name: "Batmobile", color: "Black"),
            Car_NOT_SOLID(name: "SuperCar", color: "Gold"),
            Car_NOT_SOLID(name: "FamilyCar", color: "Grey")
        ]

        cars.forEach { car in
            print(car.printDetails())
        }

        let buses = [
            Bus_NOT_SOLID(type: "School bus"),
            Bus_NOT_SOLID(type: "Minibus"),
            Bus_NOT_SOLID(type: "Minicoach")
        ]

        buses.forEach { bus in
            print(bus.printDetails())
        }
    }

}

class  Car_NOT_SOLID {
    let name:String
    let color:String

    init(name: String, color: String) {
        self.name = name
        self.color = color
    }

    func printDetails() -> String {
        return "name : \(name) color :\(color)"
    }

}


class Bus_NOT_SOLID {
    let type:String

    init(type: String) {
        self.type = type
    }

    func printDetails() -> String { 
        return "bus type : \(type)"
    }
}

Can you see?

When printData receive other type of object, we have to add more rules to work

If you want to add the possibility to print also the details of a new class, we should change the implementation of printData every time we want to log a new class

👍 Open/Closed Principle apply

protocol Printable {
    func printDetails() -> String
}

class Vehicles_SOLID {

    func printData() {
        let cars:[Printable] = [
            Car_SOLID(name: "Batmobile", color: "Black"),
            Car_SOLID(name: "SuperCar", color: "Gold"),
            Car_SOLID(name: "FamilyCar", color: "Grey"),

            Bus_SOLID(type: "School bus"),
            Bus_SOLID(type: "Minibus"),
            Bus_SOLID(type: "Minicoach")
        ]

        cars.forEach { car in
            print(car.printDetails())
        }
    }
}

class Car_SOLID:Printable {
    let name: String
    let color: String

    init(name: String, color: String) {
        self.name = name
        self.color = color
    }
    func printDetails() -> String {
        return "name : \(name) color :\(color)"
    }

}

class Bus_SOLID: Printable {
    let type: String

    init(type: String) {
        self.type = type
    }

    func printDetails() -> String {
        return "bus type : \(type)"
    }
}

We don't need change behavior of printData, just make a layer between printData and the class

L - Liskov Substitution Principle

Objects should be replaceable with instances of their subtypes without altering the correctness of that program.

Tips to apply

  1. Instead of one monolithic interface, break an interface up based on what implementers should be doing
  2. Keeps consumers from having to much power

💢 NOT Liskov Substitution Principle apply

class Rectangle_NOT_SOLID {
    var width: Double = 0
    var height: Double = 0

    var area: Double {
        return width * height
    }


}

class Square_NOT_SOLID: Rectangle_NOT_SOLID {
    override var width: Double {
        didSet {
            height = width
        }
    }
}

// MARK: - Implementations 
 func printArea(of rectangle: Rectangle_NOT_SOLID) {
     rectangle.width = 10
     rectangle.height = 4

     print(rectangle.area)
 }

 let rect = Rectangle_NOT_SOLID()
 printArea(of: rect) // 40
 let square = Square_NOT_SOLID()
 printArea(of: square ) // 40

See that printArea(of rectangle:Rectangle_NOT_SOLID) return the same result with different types instead return specific value of each class

👍 Liskov Substitution Principle apply

protocol Polygon {
    var area :Double { get }
}

class Rectangle_SOLID: Polygon {

    let width:Double
    let height:Double

    init(width: Double, height: Double) {
        self.width = width
        self.height = height
    }

    var area: Double {
        return width * height
    }

}

class Square_SOLID: Polygon {
    let side:Double

    init(side: Double) {
        self.side = side
    }

    var area: Double {
        return pow(side, 2)
    }
}

/// MARK: - Implementations  

 func printArea(of polygon:Polygon){
     print(polygon.area)
 }

 let rect = Rectangle_SOLID(width: 10, height: 40)
 printArea(of: rect) // 400.0
 let square = Square_SOLID(side: 10)
 printArea(of: square) // 100.0

I - Interface Segregation Principle

(M)any client-specific interfaces are better than one general-purpose interface

Tips to apply

  1. Instead of one monolithic interface, break an interface up based on what implementers should be doing
  2. Keeps consumers from having to much power

💢 NOT Interface Segregation Principle apply

//MARK:- Fat Interface (Protocol)
protocol GestureProtocol {
    func didTap()
    func didLongPress()
    func didSwipe()
}

class RichButton_NOT_SOLID: GestureProtocol {

    func didTap() {
        print("tap button")
    }

    func didLongPress() {
        print("long press")
    }

    func didSwipe() {
        print("swipe")
    }

}

class PoorButton_NOT_SOLID: GestureProtocol {
    func didTap() {
        print("tap")
    }

    func didLongPress() {}

    func didSwipe() {}

}

See that PoorButton_NOT_SOLID class have methods that not are usable

👍 Interface Segregation Principle apply

protocol TapGesture {
    func didTap()
}

protocol LongPressGesture {
    func didLongPress()
}

protocol SwipeGesture {
    func didSwipe()
}

class RichButton_SOLID: TapGesture, LongPressGesture, SwipeGesture{

    func didTap() {
        print("tap button")
    }

    func didLongPress() {
        print("long press")
    }

    func didSwipe() {
        print("swipe")
    }


}


class PoorButton_SOLID: TapGesture {
    func didTap() {
          print("tap button")
      }
}

Now we remove all unnecessary methods

D - Dependency Inversion Principle

High-level modules should not depend on low-level modules. Both should depend on abstractions.

Tips to apply

  1. We’re used to this : High level -> (uses) Low level
  2. High level -> (expects) interface <- ( fulfills ) low level

💢 NOT Dependency Inversion Principle apply

class SaveData_NOT_SOLID {

    let filesSystemManager = FilesSystemManager_NOT_SOLID()

    func handle(data:String){
        filesSystemManager.save(data: data)
    }
}

class FilesSystemManager_NOT_SOLID {

    func save(data:String){
        // save data
    }
}

With this, we have just a way to save Data. If we want use any DataBase?

👍 Dependency Inversion Principle apply


protocol Storage {
    func save(data:Any)
}

class SaveData_SOLID {
    let storage:Storage

    init(storage: Storage) {
        self.storage = storage
    }

    func handle(data: Any){
        self.storage.save(data: data)
    }
}

class FilesSystemManager_SOLID: Storage {
    func save(data: Any) {}
}

class MySQLDataBase: Storage {
    func save(data: Any) {}
}

Now, we can use any storage method

👊 Thanks for read!! I hope that you have understand all principles about SOLID and how apply in your day by day for build a better software. In doubt, please put your comment below and i will try to help.

Github Project


This content originally appeared on DEV Community and was authored by MKilmer


Print Share Comment Cite Upload Translate Updates
APA

MKilmer | Sciencx (2021-11-06T10:23:18+00:00) SOLID Principles in iOS Development. Retrieved from https://www.scien.cx/2021/11/06/solid-principles-in-ios-development/

MLA
" » SOLID Principles in iOS Development." MKilmer | Sciencx - Saturday November 6, 2021, https://www.scien.cx/2021/11/06/solid-principles-in-ios-development/
HARVARD
MKilmer | Sciencx Saturday November 6, 2021 » SOLID Principles in iOS Development., viewed ,<https://www.scien.cx/2021/11/06/solid-principles-in-ios-development/>
VANCOUVER
MKilmer | Sciencx - » SOLID Principles in iOS Development. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/11/06/solid-principles-in-ios-development/
CHICAGO
" » SOLID Principles in iOS Development." MKilmer | Sciencx - Accessed . https://www.scien.cx/2021/11/06/solid-principles-in-ios-development/
IEEE
" » SOLID Principles in iOS Development." MKilmer | Sciencx [Online]. Available: https://www.scien.cx/2021/11/06/solid-principles-in-ios-development/. [Accessed: ]
rf:citation
» SOLID Principles in iOS Development | MKilmer | Sciencx | https://www.scien.cx/2021/11/06/solid-principles-in-ios-development/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.