Search Your Question

SOLID Principle with Example

Ans : 

S - Single responsibility
O - Open closed
L - Liskov Substitution
I - Interface segregation
D - Dependency Inversion

1. Single Responsibility : Each and every class should have only one responsibility. We have one class which has many methods having different responsibility. If we add all methods in one class then this class looks monster.

Code for Example :

class ViewClaimDetailController {
    
    func getAllClaimDetail() {
        let jsonData = getDataFromAPI()
        let claims = parseJsonAndFillToArray(data : jsonData)
        saveDataToLocalDB(claims: claims)
       
    }
    
    private func getDataFromAPI(){
        
    }
    
    private func parseJsonAndFillToArray(data : Any) {
        
    }
    
    private func saveDataToLocalDB(claims: [Any]) {
        
    }
}

S told us that class should not be changed for more than 1 reason. 
In our above class, there are 3  private methods having different responsibility. It is very difficult for testing this class and methods as its private.

Solution is to make different classes for different behaviour or responsibilities and make one more class and call above classes methods from this class. So code is readable and also testing can be done easily.

class ViewClaimDetailController {
    
    let netcore = NetCore()
    let conversationFactory = ConversationFactory()
    let coredata = coredataController()
    
    func getAllClaimDetail() {
        let jsonData = netcore.getDataFromAPI()
        let claims = conversationFactory.parseJsonAndFillToArray(data : jsonData)
        coredata.saveDataToLocalDB(claims: claims)
    }
}

class NetCore {
    func getDataFromAPI() {
        // save to coredata
    }
}

class ConversationFactory {
    func parseJsonAndFillToArray(data: Any) {
        // save to coredata
    }
}

class coredataController {
    func saveDataToLocalDB(claims: [Any]) {
        // save to coredata
    }
}


2. Open Closed Principle : Classes and Module should be open for extension and closed for modification. According to this principle, we should write such a class or code that should not be change when new requirement comes.

Code for Example :

class Rectangle {
    var width : Double = 0
    var height : Double = 0
    
    init(inWidth: Double, inHeight: Double) {
        self.width = inWidth
        self.height = inHeight
    }
}

class AreaCalc {
    
    func calculateArea(rectangel: Rectangle) -> Double {
        return rectangel.width  * rectangel.height
    }
}

 If we want to calculate area for new shape, then we have to modify existing calculateArea() method like following :

class AreaCalc {
    
    func calculateArea(shape: AnyObject) -> Double {
        if (shape is Rectangle) {
            return shape.width  * shape.height
        } else if (shape is Square) {
            return shape.width * shape.width
        }
    }
}

Again, if want to add area of circle, triangle, we modify method and expand our if....else if... condition.

This is not good according open-closed principle. According to open-closed principle above code should be like following : 

protocol Shape {
    func calculateArea() -> Double
}

class Rectangle: Shape {
    
    var width : Double = 0
    var height : Double = 0
    
    init(inWidth: Double, inHeight: Double) {
        self.width = inWidth
        self.height = inHeight
    }
    
    internal func calculateArea() -> Double {
        return self.width * self.height
    }
}

class Circle: Shape {
    let radius : Double = 0
   
    internal func calculateArea() -> Double {
        return M_PI * radius * radius
    }
}

class AreaCalc {
    
    func calculateArea(shape: Shape) -> Double {
        return shape.calculateArea()
    }
}

We can achieve open-closed principle with help of protocol.

3. Liskov's substitution Principle : New derived classes should extend the base classes without changing the base class behaviour. Subclass should override the parent class methods in a way that doesn't break the functionality of base class from client point of view. 

Code for example : 

We have rectangle class in open-closed principle. Assume there it is not confirming Shape protocol.
Now we make square class

class square : Rectangle {
    
    override var width: Double {
        didSet {
            height = width
        }
    }
    
    override var height: Double {
        didSet {
            width = height
        }
    }
}

Now we write test cases :

func testAreaOfRectang1eFor4X3()
{
    let rectangle: Rectangle = Square()
    rectangle.height = 3
    rectangle.width = 4
    let areaCa1cu1ator = AreaCalc()
    let areaOfRectang1e = areaCa1cu1ator.calculateArea(rectang1e: rectangle)
    XCTAssertEqua1(areaOfRectang1e , 12, "Area of Rectangle not matching" )
}

Here, rectangle is of square type finding area of rectangle. As we saw override property of square, we can not lost rectangle (base) class definition and we violate liskov principle of Derived class (Square) breaking the parent class (Rectangle) funtionality of caluculating the area. 

Solution of this is only to make protocol and confirm that. That means, we can conclude that violating Liskov’s Principle violates Open Close Principle as well. 

4. Interface segregation principle : 

This principle solves FAT interface problems of Object Oriented Programming. A interface is called FAT when it has too many methods which contains more information than we really want.

Code for example : 

protocol Gesture {
    func didTap()
    func didDoubleTap()
}

class View1 : Gesture {
    
    func didTap() {
        //required this method
    }
    
    func didDoubleTap() {
        // not required this method
    }
}

class View2 : Gesture {
    
    func didTap() {
        // not required this method
    }
    
    func didDoubleTap() {
        //required this method
    }
}

Here view1 required only didTap() method and view2 require only didDoubleTap() method still both class has to implement both methods. So solution, to make two different protocol and define each method in each protocol. Class can confirm multiple protocol to achieve functionality. This is rule of interface seggregation. Or we can use @objc to make optional method.

Example 2 : 

class User {
    
    var firstName : String
    var lastName : String
    var imageURL : String
    
    init(firstName : String, lastName: String, imageURL : String) {
        self.firstName = firstName
        self.lastName = lastName
        self.imageURL = imageURL
    }
}

class UserProfileImageView {
    func loadProfilePhoto(user: User) {
        //load user.imageURL
    }
}


Here in loadProfilePhoto method, User instance is passed so all other information except imageURL are also passed. It is not good.

Solution : Use protocol 

protocol UserProfileViewDetails {
    var imageURL: String { get }
}

class User : UserProfileViewDetails {
    
    var firstName : String
    var lastName : String
    var imageURL : String
    
    init(firstName : String, lastName: String, imageURL : String) {
        self.firstName = firstName
        self.lastName = lastName
        self.imageURL = imageURL
    }
}

class UserProfileImageView {
    func loadProfilePhoto(user: UserProfileViewDetails) {
        //load user.imageURL
    }
}

Now the UserProfileImageView’s loadProfileFor(user:UserProfileViewDetails) which is the client has only the imageURL information with it to display the User Profile Image, which agrees with the Interface Segregation Principle.


5. Dependency Inversion Principle  : In the context of DIP:
  1. High-level modules (e.g., classes handling business logic) should not directly depend on low-level modules (e.g., specific implementations). Instead, they should both depend on abstractions (e.g., protocols).
  2. This promotes flexibility and reusability by allowing you to swap out low-level modules without changing the high-level logic.

Example with WiredKeyboard and WiredMouse

Without DIP (Violation of Dependency Inversion Principle)

Here, the Computer class directly depends on the concrete classes WiredKeyboard and WiredMouse. This makes it difficult to replace or extend these peripherals.

class WiredKeyboard {
func type() { print("Typing using a wired keyboard.") } } class WiredMouse { func click() { print("Clicking using a wired mouse.") } } class Computer { let keyboard: WiredKeyboard let mouse: WiredMouse init(keyboard: WiredKeyboard, mouse: WiredMouse) { self.keyboard = keyboard self.mouse = mouse } func useComputer() { keyboard.type() mouse.click() } } // Usage let keyboard = WiredKeyboard() let mouse = WiredMouse() let computer = Computer(keyboard: keyboard, mouse: mouse) computer.useComputer()

Problem:

  • If we want to use a wireless keyboard or mouse, we would have to modify the Computer class, violating the Open/Closed Principle as well.

With DIP (Using Abstractions)

Here, we introduce protocols (Keyboard and Mouse) as abstractions. The Computer class depends on these abstractions rather than specific implementations.

protocol Keyboard {
func type() } protocol Mouse { func click() } class WiredKeyboard: Keyboard { func type() { print("Typing using a wired keyboard.") } } class WiredMouse: Mouse { func click() { print("Clicking using a wired mouse.") } } class Computer { let keyboard: Keyboard let mouse: Mouse init(keyboard: Keyboard, mouse: Mouse) { self.keyboard = keyboard self.mouse = mouse } func useComputer() { keyboard.type() mouse.click() } } // Usage let wiredKeyboard = WiredKeyboard() let wiredMouse = WiredMouse() let computer = Computer(keyboard: wiredKeyboard, mouse: wiredMouse) computer.useComputer()

Benefits of Using DIP

  1. Flexibility: You can easily introduce new peripherals without changing the Computer class.

    class WirelessKeyboard: Keyboard {
    func type() { print("Typing using a wireless keyboard.") } } class WirelessMouse: Mouse { func click() { print("Clicking using a wireless mouse.") } } let wirelessKeyboard = WirelessKeyboard() let wirelessMouse = WirelessMouse() let anotherComputer = Computer(keyboard: wirelessKeyboard,
    mouse: wirelessMouse) anotherComputer.useComputer()
  2. Testability: You can mock Keyboard and Mouse for unit testing the Computer class without needing actual implementations.

Summary : 
  • Each an every class should have only one responsibility
  • Should not modify the existing class for change requirement, rather extent the class
  • Extending or Inheriting a child/derived class should not break any parent or base class functionality
  • The Interface or class API’s to client should have minimum information required by the client
  • Class A should not depend on Class B or vice versa, both should be losely coupled


If you have any comment, question, or recommendation, feel free to post them in the comment section below!   

I have 100 cells. In each cell I have 5 button. How can I check which cell button is clicked?

Ans : 

There are various approach for this problem. But following two approach are better.

Approach-1 : Delegate - Protocol 

Cell : 

protocol CellSubclassDelegate: class {
    func buttonTapped(cell: CellSubclass)
}

class CellSubclass: UITableViewCell {

@IBOutlet var someButton: UIButton!

weak var delegate: CellSubclassDelegate?

override func prepareForReuse() {
    super.prepareForReuse()
    self.delegate = nil
}

@IBAction func someButtonTapped(sender: UIButton) {
    self.delegate?.buttonTapped(self)

}

ViewController:

class MyViewController: UIViewController, CellSubclassDelegate {

    @IBOutlet var tableview: UITableView!

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! CellSubclass

        cell.delegate = self


    }

    func buttonTapped(cell: CellSubclass) {
        guard let indexPath = self.tableView.indexPathForCell(cell) else {
            // Note, this shouldn't happen - how did the user tap on a button that wasn't on screen?
            return
        }

        //  Do whatever you need to do with the indexPath

        print("Button tapped on row \(indexPath.row)")
    }


Approach-2 Using Closure:

Cell :

class MyCell: UITableViewCell {
    var button: UIButton!

    var buttonAction: ((Any) -> Void)?

    @objc func buttonPressed(sender: Any) {
        self.buttonAction?(sender)
    }

}


ViewController : 

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! MyCell
    cell.buttonAction = { sender in
        // Do whatever you want from your button here.
    }
    // OR
    cell.buttonAction = buttonPressed(closure: buttonAction, indexPath: indexPath) // <- Method on the view controller to handle button presses.

}

No need to explain code. Simple as that.

If you have any comment, question, or recommendation, feel free to post them in the comment section below!


What is Operator overloading?

Ans : 
Operator overloading is the practice of adding new operators and modifying existing ones to do different things. Operators are those little symbols like +*, and /, and Swift uses them in a variety of ways depending on context – a string plus another string equals a combined string, for example, whereas an integer plus another integer equals a summed integer.
To create a new operator, try adding this to a playground:
infix operator **
That’s the exponentiation operator, designed to raise one number to the power of another. Normally we’d use the pow() function for that job, but with operator overloading we can make ** work instead.
Now you need to tell Swift what to do when it sees that operator. For example, when we write something like 2 ** 4 what does that mean?
Syntax of making operator : 
func **(lhs: Double, rhs: Double) -> Double {
    return pow(lhs, rhs)
}
Use : 
let result = 2 ** 4
We can specify associativity and a precedence group also but it is very deep level.
If you have any comment, question, or recommendation, feel free to post them in the comment section below!

If there are two views up and bottom, up-view is 60% of superview and 40% of superview respectively, then what to do with constraint?

Ans : 


Use of multiplier in constraint in iOS
Use of multiplier in constraint


We took 2 views - Orange and Green. 
We set orange view's top, leading, trailing constraint to main view as 0. 
Same, we set green view's bottom, leading, trailing constraint to main views as 0.

Now,
We select height constraint of orange view equal to main view's height. And set multiplier 0.6. It shows ratio of orange view's height to main view's height. We have set 0.6 = 60/100 . So orange view's height become 60% of main view's height.

Set multiplier of height constraint in size inspector
Set multiplier of height constraint in size inspector


Same we have done with green view and set multiplier 0.4 = 40/100 means 40%.

If you have any comment, question, or recommendation, feel free to post them in the comment section below!


What is meaning of _ in Swift func ?

Ans : 


The _ is used to define that the parameter is not named
If you have multiple _ it states that you do not need to name the parameters in your function call
func myFunc(name:String, _ age:String){       }

myFunc(Milo", "I'm a really old wizard")
If you do not use the underscore you would use 
myFunc(Milo, age: "I'm a really old wizard")
The _ is not necessary in function calls. It is just used to indicate that something does not to have a name.



Explain application states in iOS

Ans : 

There are 5 application states :

  1. Not Running
  2. Inactive
  3. Active
  4. Background
  5. Suspended.
iOS Application states
Application States


iOS Application Lifecycle : 

When an iOS app is launched the first thing called is

Initialise :

application: willFinishLaunchingWithOptions:-> Bool.
This method is intended for initial application setup. Storyboards have already been loaded at this point but state restoration hasn’t occurred yet.

Launch :

application: didFinishLaunchingWithOptions: -> Bool is called next. This callback method is called when the application has finished launching and restored state and can do final initialisation such as creating UI.

applicationWillEnterForeground: is called after application: didFinishLaunchingWithOptions: or if your app becomes active again after receiving a phone call or other system interruption.

applicationDidBecomeActive: is called after applicationWillEnterForeground: to finish up the transition to the foreground.

Termination :

applicationWillResignActive: is called when the app is about to become inactive (for example, when the phone receives a call or the user hits the Home button).

applicationDidEnterBackground: is called when your app enters a background state after becoming inactive. You have approximately five seconds to run any tasks you need to back things up in case the app gets terminated later or right after that.

applicationWillTerminate: is called when your app is about to be purged from memory. Call any final cleanups here.


Both application: willFinishLaunchingWithOptions: and application: didFinishLaunchingWithOptions: can potentially be launched with options identifying that the app was called to handle a push notification or url or something else. You need to return true if your app can handle the given activity or url.

Above all methods are defined in UIApplicationDelegate and AppDelegate.swift has to implement these methods.

If you have any comment, question, or recommendation, feel free to post them in the comment section below!