October 5, 2020

How to pass data from the model to controller in iOS

By Mohit Agrawal

Three ways of communication from model to the controller

model view controller

Being an iOS developer or a software engineer we always face a situation where we need to transfer the data between two different controllers. Today in this article we will how to pass the data from the model to the controller.

I am assuming that you do use MVC or MVVM architecture in your projects. If you are using a single UIViewController for requesting, receiving, parsing, and updating UI then you should be considering using proper architecture like MVC or MVVM.

I will show you three different ways of passing data from model to controller:

  1. Using Callbacks
  2. Using Delegation
  3. Notification and Observer

We will see all three ways one by one and try to understand the concept in the detail with the help of code snippets. By the end of this tutorial, you will able to decide which one fits the best for your project.

To understand the concept we will take a ViewController and a Data model class. In any of the ways, the main aim will be to pass the generated data from the data model class to the view controller. It does not matter that what is the source of data, it could be anything like an HTTP response or local JSON file.

Let’s create our two classes: ViewController and DataModel.

class ViewController: UIViewController {
}

class DataModel {
}

Callbacks and Completion Handlers

Let’s start with the easiest way of passing data from the model to the view controller. First, create a getData method that takes a completion as shown in the below code snippet.

class DataModel {
        func getData(completion: ((_ data: String) -> Void)) {
          
        }
    }

Completion in the getData method takes a string as data and return void.

Now we need to call the completion block to send the data. Let’s do this.

class DataModel {
       func getData(completion: ((data: String) -> Void)) {
          // the data was received and parsed to String
          let data = "This is Warmodroid"
          completion(data)
       }
    }

Now, in the next step, we need to create an object of the data model in the view controller. Call the getData method and in the completion use the data received from the model.

    class ViewController: UIViewController {
       private let dataModel = DataModel()
       override func viewDidLoad() {
          super.viewDidLoad()
          dataModel.getData { [weak self] (data: String) in
                self?.useData(data: data)
          }
       }
       private func useData(data: String) {
           print(data)
       }
    }

As you can see, we are calling the useData method and printing the data. Now you can run your code and output in the console. This is one of the ways of passing the data from the model class to the view controller.

Delegations

The previous one is the easiest one. But if we talk about the most commonly used method of passing the data then Delegations is the one.

protocol DataModelDelegate: class {
    func didRecieveDataUpdate(data: String)
}

Now we need to take care of memory leak. We should not create a retain cycle between the delegate and the delegating objects, so we use a weak reference to delegate.

Create a weak delegate in the DataModel.

weak var delegate: DataModelDelegate?

And this is how we will use this in the getData method.

    class DataModel {
          weak var delegate: DataModelDelegate?
          func getData() {
             // the data was received and parsed to String
             let data = “Data from wherever”
             delegate?.didRecieveDataUpdate(data: data)
          }
    }

Next, create an object of the DataModel in the ViewController. Now assign the delegate to self and call getData.

class ViewController: UIViewController {
      private let dataModel = DataModel()
      override func viewDidLoad() {
         super.viewDidLoad()
         dataModel.delegate = self
         dataModel.getData()
      }
}

In order to confirm the DataModelDelegate protocol, we need to implement this didRecieveDataUpdate method to our controller.

extension ViewController: DataModelDelegate {
      func didRecieveDataUpdate(data: String) {
         print(data)
      }
}

You can run the code see the printed data in the log. But did you get the difference between callbacks and delegations?

If you ask me the I would say delegation is a much easier way of passing data from the model to the controller. Just you have to create a class that confirms the protocol. And it also reduces the code redundancy. You can also share your thoughts in the comment section 😎.

Notification and Observer

This is a very rarely used method. Apple also does not recommend it to use. But sometimes we can not escape from it even if we do not want to use it.

Suppose you have a shared data source and you want to use it throughout the app. This is one possible scenario you will have to use notification to pass the data.

Let’s understand this with an example. If we need to retrieve a lot of locally stored user images and use them in multiple ViewControllers then using delegation would require every single ViewController to conform to its protocol. But we use the notification then we can do this in a more elegant way.

First we need to create a DataModel class as a singleton class.

class DataModel {
   static var sharedInstance = DataModel()
   private init() { }
   
   private (set) var data: String?
   func getData() {
   }
}

After receiving the data, we need to store it in local variable.

func getData() {
   // the data was received and parsed to String
   self.data = “Data from wherever”
}

After updating the local data, we need to post a Notification. The best way to do this will be by using a property observer. Add didSet property observer to the data variable.

private (set) var data: String? {
   didSet {
      
   }
}

Every notification needs a unique name. so let’s create a string literal outside of DataModel class.

let dataModelDidUpdateNotification = “dataModelDidUpdateNotification”

Now we can set the notification like this.

private (set) var data: String? {
   didSet {
      NotificationCenter.default.post(name:  
NSNotification.Name(rawValue: dataModelDidUpdateNotification), object: nil)
   }
}

Let’s see the explanation of the above code. We are triggering the notification as soon as we are setting a new value to the data variable. It means it will inform every view controller which is listening to this notification.

Let’s implement the observer for this notification in our view controller.

class ViewController: UIViewController {
     override func viewDidLoad() {
         super.viewDidLoad()
         NotificationCenter.default.addObserver(self, selector: #selector(getDataUpdate), name: NSNotification.Name(rawValue: dataModelDidUpdateNotification), object: nil)
     }
} 

Now the observer will listen to any updates in DataModel and call the getDataUpdate method on every change. Let’s implement this method.

@objc private func getDataUpdate() {
      if let data = DataModel.sharedInstance.data {
         print(data)
      }
}

Technically, if we compare notification with callbacks and delegation then we will find that it does not really pass the data between model and view controller. It just informs everyone that new data is available.

We should be a little careful while using the notification and observer. We should always remove the observer when it is no longer needed. Otherwise, it will utilize the unwanted RAM.

To remove the observer when it is not need we can simply use the deint().

deinit {
      NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: dataModelDidUpdateNotification), object: self)
}

The last step is to call the getData method, using a sharedInstance of DataModel.

class View Controller: UIViewController {
   override func viewDidLoad() {
        super.viewDidLoad()
         NotificationCenter.default.addObserver(self, selector: #selector(getDataUpdate), name: NSNotification.Name(rawValue: dataModelDidUpdateNotification), object: nil)
        DataModel.sharedInstance.getData() 
    }
}

Run your project and you should be able to see the output in the log.

Your next destination

After learning how to pass data from model class to the view controller, you can learn how to get data from API and display it in the app.

Conclusion

Subscribe YouTube: More tutorials like this

I hope this blog post is useful for you, do let me know your opinion in the comment section below.
I will be happy to see your comments down below 👏.
Thanks for reading!!!