iOS Design Patterns for Data Communication

iOS design patterns have evolved a lot over the years. In the three short years I've been involved in the mobile dev world, different trends have come and gone and best practices once used by many have been replaced by newer, more efficient ways of solving a problem. If you work in software development you know this... no big deal... that's just how it is. I agree. I wanted to lay out a few of the patterns for my own benefit, and include a look into one of my favorite new* patterns, functional reactive programming.

There are several different approaches you can take in communicating information between objects. Borrowed from NSHipster, this list highlights differences in coupling and intended audience under different approaches.

  • Intimate (One-to-One)
    • Weakly coupled
      • Target-Action
      • Delegate
      • Callbacks
    • Strongly coupled
      • Direct method invocation
  • Broadcast (One-to-Many)
    • Weakly coupled
      • Notifications
    • Strongly coupled
      • Key-value Observing

I don't intend for this post to only hit the listed patterns, and accordingly don't plan to just walk through each of these approaches. However, I will briefly touch on several and add some thoughts on a few other patterns along the way.

Target-Action

The target-action pattern is probably most familiar from using different UI controls hooked up to their respective IBActions. When a target receives an event, such as a button receiving a tap or a date picker landing on a new date, an action is triggered.

Delegation Pattern

The trusty delegation pattern. I'll never forget this question from a job interview with Apple while I was still a student at BYU. Why are delegates protocols? Delegates are extremely powerful because any object can say hey, I conform to this protocol, I'll be the delegate. Delegates can also conform to multiple protocols (friendly tip: this was the answer the interviewer was looking for). If delegates were subclasses instead of protocols then the view controller would be forced to choose one set of instructions to support. It's not uncommon for an object to conform to 3, 4, or 5. The delegating object keeps a reference to the delegate object and sends messages to it at the appropriate time. Some of the more common UIKit protocols include UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate, and UIAlertViewDelegate.

Blocks

Delegates are so old school, who uses them anymore? I mean, srsly:

Blocks are nice to use because they can keep the completion code/logic nestled up nice and close to the responsible party. In the words of Ryan Hodson:

Blocks are Objective-C’s anonymous functions. They let you pass arbitrary statements between objects as you would data, which is often more intuitive than referencing functions defined elsewhere.

For simple tasks it can end up feeling a lot cleaner than using delegate counterparts or creating and calling private methods. One downside: the syntax is impossible to remember. F***ing Block Syntax is your friend.

Notifications

The notification pattern is so core to Cocoa that I think I've yet to see a code base that doesn't use NSNotificationCenter. As the list above suggests, notifications are a broadcast form of communication. A notification is sent out and any object listening for it at that time will receive it and can respond accordingly.

While powerful, I've seen notifications abused more than once or twice. What happens is the developer thinks to him or herself, "hmmm, I could do it this (other, more preferable) way, ORRR I could just setup a notification and be listening for it in this one (or five) other places and be done with it." The problem is that program execution suddenly goes from readable, flowing, easy-to-follow, to JMP over here and JMP over there. I'm not saying don't use NSNotificationCenter, but know when and when not to. If you are setting up notifications, listening for more notifications, and routing those to other view controllers all in your app delegate... you may be doing it wrong. Want a good use case for notifications? Currently I use NSNotificationCenter to notify listeners that network reachability has changed. It seems (to me) to be an appropriate use of a one-to-many broadcast.

KVO

Key-value observing is another of those patterns entrenched deep in the heart of Cocoa. The biggest problem in my book: it's extremely easy to write bugs into KVO code. The first time I did Startup Weekend I joined a team with an experienced, long-time Mac dev and KVO was almost the death of me that weekend. The idea is powerful: observe keypaths and get notified about state changes in any other object. At CocoaHeads last week we were on the topic of KVO in Swift and began hopeful speculation for next year's WWDC providing a new API built on top of KVO that makes KVO super nice in Swift.

ReactiveCocoa

Speaking of super nice KVO... enter ReactiveCocoa (RAC). What is RAC? The first question is what is functional reactive programming (FRP)? The best explanation of FRP I've hear thus far, from one of the creators of RAC:

Programs take input and produce output. The output is the result of doing something with the input. Input, transform, output, done.

The input is all the sources of action for your app. It’s taps. It’s keyboard events. It’s timer triggers, GPS events, and web service responses. These things are all inputs. They all feed into the app, and the app combines them all in some way to produce a result: the output.

The output is often a change in the app’s UI. A switch is toggled or a list gets a new item. Or it could be more than that. It could be a new file on the device’s disk, or it could be an API request. These things are the outputs of the app.

But unlike the classic input/output design, this input and output happens more than once. It’s not just a single input → work → output—the cycle continues while the app is open. The app is always consuming inputs and producing outputs based on them.

To put it another way, the output at any one time is the result of combining all inputs. The output is a function of all inputs up to that time.

When I mentioned FRP in the first paragraph I put an asterisk next to new. That's because FRP is not new. The principles of reactive programming have existed since at least the 90s, and KVO in many ways accomplishes similar goals in eliminating state and observing objects for changes. ReactiveCocoa includes a powerful API that achieves many of the goals in reacting to property changes but without the pain points of KVO.

The de facto RAC example seems to be observing a UITextField (or perhaps many) for text changes and updating say, a login button, to an enabled/disabled state based on the contents of those text fields. The same approach without RAC requires, no joke, checking the contents of fields in viewDidLoad as well as the use of at least two different UITextField delegate methods (namely, textField:shouldChangeCharactersInRange: and textFieldShouldClear:).

One of my favorite other use cases so far is reloading tables in UITabBarController's root view controllers. If you've used UITabBarController before, you may have been surprised, confused, frustrated (or just not cared) to find out that the root view controllers in each tab are only loaded once (or you may have been smarter than me and realized from the beginning that it actually makes more sense that way). You don't get a full refresh of the view lifecycle, so you can't rely on viewDidLoad being called... ever again, really, unless the app is FCed. But at the same time putting a fetch operation in viewWillAppear just feels like a bad idea, right? Do you really want to fetch all that data again if the user leaves the screen and comes back 2 seconds later? Of course not. Okay what next... setup an NSTimer or some sort of timestamp and only fetch it if it's been at least 60 seconds? All that hairiness for something as simple as fetching some data? And what if on top of that you fetch that same data on a different screen or on appDidResume, how do you notify that view controller?

ReactiveCocoa has made my life so much easier when it comes to this. I setup an observer for the data cache property that feeds the table on that screen, then simply fetch ze datas on viewDidLoad. Once the data returns and updates the cache, the observer reacts to that change and reloads the tableview. I've also added a call to fetch that same data on applicationDidBecomeActive so it's periodically updated. It works great, is super clean, and avoids some ugliness in subclassing UITabBarController and using timers.

Summing it up

Functional reactive programming and ReactiveCocoa is the latest of many new patterns to influence iOS development and Objective-C. It borrows good ideas from other paradigms and brings new solutions for solving familiar problems. Each of the patterns mentioned above have their own characteristics and intricacies. I'm not advocating for a complete functional revolution, but learning functional programming does make you a better programmer. For those who just haven't bought into the FRP hype, I leave you with this story from Rob Napier's post, Swift is Not Functional:

I used to ride a motorcycle. When you’re riding a motorcycle, you have to be much more aware than when you drive a car. If you’re not, you’re going to get hurt. But the thing I noticed was that riding a motorcycle made me a better car driver, too. Functional programming is like that. Riding a motorcycle isn’t always the best way to get somewhere, but we’d have better drivers if everyone learned on motorcycles. We’d have better programmers if colleges taught Haskell first and Java later.
  And a picture of my motorcycle for good measure.

And a picture of my motorcycle for good measure.