Showing posts with label ios. Show all posts
Showing posts with label ios. Show all posts

Thursday, October 3, 2019

Custom cell class datasource in MVVM design pattern



We generally pass a model to cell to configure/render and the reason behind it is, instead of passing multiple parameters, we can have a model to be passed as parameter.


Let’s take a below cell for example which is having 4 labels and in MVVM design pattern, viewmodels build and gives model to cell in cellForRowAt in viewcontroller.





This seems/sounds familiar as this is what we have been doing.
But, there are flaws in this approach.

  1. What If I want to reuse the cell?
  2. In MVVM design pattern view(in this case cell) should not hold a model(Agree?)



I can not reuse the cell efficiently as this cell is tightly coupled with a model.

First in place, this poor cell is having 4 labels and technically needs 4 stings to display. As simple as that. Isn’t it?

Then why to pass a model and create a tight coupling.

Instead of passing a model, Cell can have a datasource implemented like below.




Now cell configuration happens based on the ProductCellDataSource protocol. That means any model that confirms to this protocol can be passed to cell configure method to render the cell.




In other words, cell is just asking to send me the data as per my protocol and it is saying I am not literally worried about the model you are sending as long as it confirms to my datasource protocol.




With this approach, cell will not have any tight coupling with models(MVVM basic rule) and it can be reused by passing any model which confirms to it’s datasource protocol.

Hope this post is useful. Feel free to comment incase of any queries.


Wednesday, September 26, 2018

Swift - Multiple levels of Optional Binding


If there is Optional, We tend to bind them using guard or if let multiple levels which should be based on what we need exactly.

     
  class Person {

    var name: String?
    weak var spouse: Person?
    
      init(name: String) {
          self.name = name
      }

   }

    let wife = Person(name: "Wife")
    let husband = Person(name: "Husband")
    wife.spouse = husband
    husband.spouse = wife

  

I need to display spouse name in a label.

We don’t need two bindings like below.

     
   if let spouse = husband.spouse, 
                   let name = spouse.name {
        lblSpouseName.text = name
   }

  
Cause, We are not using binded spouse(let spouse = husband.spouse) object. We are using only minded spouse name(let name = spouse.name)

As here what we need is only spouse name, We can take help of optional chaining in optional binding.

     
  if let name = husband.spouse?.name {
      lblSpouseName.text = name
  }

  

husband.spouse?.name is optional chaining and gives value only If husband’s spouse is not nil and spouse name is also not nil.

That’s how we need to use binding for the things we need in the scope of If let or guard let.

In the above case, Coalescing operator works like charm and best fit where we can give a default value as well.


     
  lblSpouseName.text = husband.spouse?.name ?? "NA"
    
  

Hope this post is useful. Feel free to comment incase of any queries.



Swift - App is currently in Main Thread or not


At any point of time, we can check whether we are in main thread or not.


     
    if Thread.current.isMainThread {
        print("isMainThread")
    }

  
This is useful for generic UI operations for async calls.

Suppose, we are need to display generic alerts for network errors and we wrote a common method for that,


     
   func networkError() {
        //stop loader
        //display alert
    }



Some developers forget to write UI calls on main thread in network calls.


     
 let task = URLSession.shared.dataTask
  (with: url) { ( data, responseData, error) in
                 
            if error != nil {
                networkError()
            }

        }



So, We can check whether we are in main thread or not in  networkError() method and do UI stuff like shown below.


     
 func networkError() {

        if Thread.current.isMainThread {
            //stop loader
            //display alert
        } else {
            DispatchQueue.main.async {
                //stop loader
                //display alert
            }
        }
    }



Hope this post is useful. Feel free to comment incase of any queries.



Wednesday, August 29, 2018

Swift Nil-Coalescing Operator for Optionals


Let’s see how to use Coalescing operator for optionals in swift.


     
  var name: String?
  label.text = name ?? ""



Here ?? is the Coalescing operator which  checks whether optional is nil or not.

If Nil, takes default value, If not takes optional.

In short, below is what it does.


     
  if name != nil {
     //use name!
  } else {
     //use default value
  }



Nil-Coalescing Operator is very useful for checking optional and giving default value in a single statement instead of going with optional binding.

     
  if let nameVal = name {
       label.text = nameVal
   } else {
       label.text = “”
   }



We can not use ternary operator for optionals. Below gives a build error.

     
  label.text = name ? name! : “”





Hope this post is useful. Feel free to comment incase of any queries.



Tuesday, August 28, 2018

Protocol Extensions for Default and Convenient API


In this tutorial, let’s see how protocol extensions can be used as default and convenient API.

I am taking a Movable protocol which is having a move method.


     
  protocol Movable {
    func move(at minimumSpeed: Float)
  }


I am creating a class called Car which confirms to Movable protocol.


     
  class Car: Movable {
    
   }


This gives a build error.



We need to implement move method as Car is confirming to Movable.

Instead of this forced implementation, using protocol extensions, we can write default implementation.


     
  extension Movable {

    func move(at minimumSpeed: Float) {
      print("Moving @ \(minimumSpeed) MPH")
    }

  }

  let car = Car()
  car.move(at: 40.0)


This is how we can leverage protocol extensions for default implementations.

Now, What If I don’t want to mention the speed every time. I need to go with minimum speed 40.0 as default speed.

This can also be done using the protocol extensions, by having default value for minimumSpeed.


     
  extension Movable {

 func move(at minimumSpeed: Float = 40.0) 
  {
     print("Moving @ \(minimumSpeed) MPH")
  }

 }

 let car = Car()
 car.move()


Simple and useful. Isn’t it?

Hope this post is useful. Feel free to comment incase of any queries.


Enum raw value comparisons


It’s a good practice to take an enum for TableView sections instead of writing if conditions for indexPath.section


     
  if section == 0 {} 

  if section == 1 {} 



Instead of this take an enum like below for example as a Profile screen.


     
 enum ProfileSections: Int {
    case Profile = 0,
    Info,
    Friends
 }



Now, usually what we do is like below,


     
 if section == ProfileSections.Profile.rawValue 
  {print("Profile Section")}

 if section == ProfileSections.Info.rawValue 
  {print("Info Section")}

 if section == ProfileSections.Friends.rawValue    
  {print("Friends Section")}



If we write like this, there is no point of taking an enum. Because It’s same like,


     
 if indexPath.section == 0 {} 

 if indexPath.section == 1 {} 



Agree?

The better approach would be below.


     
 let section = 2

 if let profileSection = ProfileSections(rawValue: section) {

    switch profileSection {

    case .Profile:
        print("Profile Section")

    case .Info:
        print("Info Section")

    case .Friends:
        print("Friends Section")

    }

 }



Convert the section to ProfileSections and write a switch case.

Incase you will need to add a new section like Bio, It’s as simple as adding another switch case.

     
 enum ProfileSections: Int {
    case Profile = 0,
    Info,
    Bio,
    Friends
 }

 if let profileSection = ProfileSections(rawValue: section) {

    switch profileSection {

    case .Profile:
        print("Profile Section")

    case .Info:
        print("Info Section")

    case .Friends:
        print("Friends Section")

    case .Bio:
        print("Bio Section")

    }

 }



The beauty here, with switch, we get a build error for missing case :)


     
 // note: add missing case: '.Bio'
    switch profileSection {
    ^



Hope this post is useful. Feel free to comment incase of any queries.