Ways to do callback, when and where

asynchronous-programmingdesign-patternsiosswift-language

I've recently started to do some more complicated programming with network-related work on iOS with Swift.
This introduced a lot of asynchronous code not only in networking but also some exchange of information between view controllers.

Anyway, my question is simple, and can be quite general. As far as I know, there are (at least) four ways to do callback, so is there a general rule as to which is better in each case?

  • callback closure (function) as a function argument
  • delegation with protocol
  • notification (or observer)
  • shared state object

Use cases in my application

  • Network stuff, POST request or download file
  • passing data one-way between view controllers on button tap
  • passing data both-ways between view controllers
    (One view shows the selected option, another view gives the real options for user to select)

Best Answer

Here are my views on the various observation patterns. I may update this list over time if comments seem to require it.

  1. Callback with closure: This is great if you have some sort of one-shot thing (like a network response), or multiple responses that all need to be handled the same way. Not so good if you are expecting multiple responses which need to be handled differently or different types of responses (which would require a different closure for each type of response.) Also not helpful if you have multiple objects interested in the response.
  2. Delegation with protocol: Makes a lot of sense if there is a business reason why there should only be one listener/observer and it makes sense for that observer to handle several different state changes. In other words, if you have more than one object that is interested in the subject, then delegation won't work. If you end up with a protocol that only forces the implementation of one method, then this pattern is likely not a good idea either. Also, if you have an observer that wants to track several different objects that implement the protocol in question, then you should probably reconsider the use of this pattern.
  3. Notification: The NSNotificationCenter works pretty well for very generic or system wide messages. If you only have one observer, or only expect the message to be sent once, then this is a poor choice of pattern. Also, this pattern can save you a lot of refactoring when you have to send a message between two disparate objects in a mature system (i.e., a hack.)
  4. Key value Observation: This works well if the observer only wants to monitor the state of a single field but can get unwieldy if trying to observe several fields or if you just want to be informed of some signal.

Some patterns you didn't mention:

  1. target/action callback: An example of this is UIControl... This has the same benefits and tradeoffs as a callback with closure and I think has largely been superseded by it.
  2. Abstract method/subclass instantiation: There is the classic mode of implementing a method in a subclass to handle requirements of the base class. UIViewController's viewDidLoad and the like are examples. Another pattern that has been largely superseded, by protocols, but comes in handy if you happen to already have the class structure in place.
  3. Futures/Promises: This pattern has the same benefits and drawbacks as callback closures, but can avoid nested closures that sometimes pop-up.

Lastly, there is reactive programming (ReactiveCocoa and RxSwift for example) that can potentially take the place of all of the above patterns. It takes some time to learn but once you get the hang of it, you can handle all your observation needs with one pattern.

Related Topic