Notifications

Using notifications works well when you have a lot of observers that need to listen for an update. For instance, if you have 5 classes that all load saved data then you can alert all 5 classes to a successful network request at once.

The basic syntax:

Note: The “@objc” tag is required in front of any function that is called through the #selector.

Notifications are not optimal for sending data (though it is possible) but, rather, are best used to alert a class that new data is available. Say you have a data model that downloads network data, once this data is saved to memory you alert your view controllers to propagate this newly saved data into view.

Here is an example of a working notification structure:

Delegation

Delegation is the method I use most often for communicating between my Model, View and Controller classes. The delegation design pattern uses a protocol to define methods, properties and the other requirements necessary for a class to implement. I like to think of a protocol as a contract — by fulfilling the contract laid out in a protocol, you are obliged to implement its requirements.

To implement the delegation pattern you must initialize a protocol:

protocol ModelDelegate: class {

func didReceiveData(_ data: String)

}

In the model class (the class that will hold this delegate) you must initialize a weak reference to the ModelDelegate.

The reference to the delegate must be weak or unowned. By using a strong reference you may find yourself creating a retain cycle where the delegate holds a reference to the parent class and the parent class to the delegate. By creating a strong reference cycle, both objects hold references to each other in memory, thus not allowing ARC to deallocate the delegated object upon its de-initialization.

class Model {

weak var delegate: ModelDelegate? func downloadData() {

let data = "Network request information."

delegate?.didReceiveData(data)

}

}

Now create an instance of Model in its Controller class and assign its delegate to self .

class ViewController: UIViewController {

let model = Model()



override func viewDidLoad() {

super.viewDidLoad()

model.delegate = self

model.downloadData()

}

} extension ViewController: ModelDelegate {

func didReceiveData(_ data: String) {

print(data)

}

}

In order to assign the model’s delegate to self ( self = the ViewController class in this example) the ViewController must conform to the ModelDelegate protocol. We can achieve this by adding an extension of view controller that adopts the protocol in order to conform to the layout defined in ModelDelegate .

In this example, the ViewController holds an instance of a Model . Once the model has successfully downloaded data it will call the function “ didReceiveData ” inside the ViewController . Once this function has been called you can update your View class within the ViewController .

Callbacks

Callbacks are simple to set up. I don’t often use them as I prefer the rigidity of the delegation pattern. However, they are useful for simple communication.

In a View class we have a function that runs an animation. Upon the completion of the animation we want to alert our ViewController that the animation has finished.

class View {

func runAnimation(completion: @escaping() -> ()) {

//run animation, upon completion send callback

completion()

}

}

In the ViewController create an instance of the View class and call the runAnimation() function:

Take special note of the “ [weak self] in” inside the closure on runAnimation() . By specifying that self is a weak reference, we avoid retain cycles. If we capture self strongly then the closure would hold a strong reference to self, thus not allowing deallocation in memory.

It is also possible to send data directly through a callback: