A Very Narrow Usage of Storyboards
Lately I’ve been struggling to balance the pros and cons of various view controller initialization techniques. There are three, none of which are perfect:
Doing it All In Code - PRO: I have complete control over when and how my dependencies and views get initialized. CON: Some complex view hierarchies are easier to manage using the Interface Builder GUI (primarily conditional edits for different size class combinations).
NIBs - PRO: I’m able to pass in dependencies to parent view controllers (Core Data stack access, delegates, API clients, etc) at initialization time. Since I work in Swift, this helps avoid inconvenient optionals or dangerous force-unwrapped optionals. CON: NIBs lack some useful features that Storyboards have, like layout guides or automatic cell nib registration.
Storyboards - PRO: All the GUI benefits of NIBs, with addtional perks like top/bottom layout guides and automatic cell nib registration. CON: Storyboards are prone to a whole host of other shortcomings which I won’t list here (a few of which are also shared by NIBs). The most problematic shorticoming is that, unlike NIBs, UIViewController has no equivalent
initWithStoryboard()method. Why does the lack of aninitWithStoryboard()method matter? It means you cannot provide a custom init method for your class, which means you cannot pass in dependencies at initialization time. This is true even if you don’t use segues and only use one view controller per storyboard. This implies that the view controller’s dependencies are public read/write properties. It’s an antipattern for parents to have that much introspective knowledge about how to configure their children. Public read/write dependencies also expose a surface area for bugs from API misuse. Exposing an otherwiseprivate letdependency as a publicvarimplies that the property is free to use.
Lately, the projects I’ve been working on have had pretty heavy AutoLayout requirements. As such, it’s been easiest to build the user interface logic using a Storyboard. But the Storyboard approach makes it really hard to pass in dependencies in a simple, reliable fashion.
Until Apple provides a better solution, my current strategy is to provide a static factory method that takes as arguments all the dependencies that need to be passed in:
static func newFromStoryboard(foo: FooDependency, bar: BarDependency) -> MyViewController
In addition, I make the relevant instance variables private, so that they can only be set from inside the file:
private var foo: FooDependency! private var bar: BarDependency!
The factory method instantiates the view controller from the correct storyboard, and then sets all the dependencies. I only use one view controller per storyboard, and never use segues.
This is not the most elegant approach I can imagine, but I believe it’s the least inelegant approach made possible within the current state of the UIKit framework and Swift.
Don DeLillo on Chickenshit White UI Design
We ran into Murray Jay Siskind at the supermarket. His basket held generic food and drink, nonbrand items in plain white packages with simple labeling. There was a white can labeled CANNED PEACHES. There was a white package of backon without a plastic window for viewing a representative slice. A jar of roasted nuts had a white wrapper bearing the words IRREGULAR PEANUTS. Murray kept nodding to Babette as I introduced them.
“This is the new austerity,” he said. “Flavorless packaging. It appeals to me. I feel I’m not only saving money but contributing to some kind of spiritual consensus. It’s like World War III. Everything is white. They’ll take our bright colors away and use them in the war effort.”
From White Noise.
I admit I never understood what Manton found appealing about microblog posts until today, a week...
I admit I never understood what Manton found appealing about microblog posts until today, a week into my Giving Up On Twitter experiment, inspired by Seth. Seth’s feelings about Twitter caught me off guard. I think I’ve been having a similar experience. So I deleted all but Tweetbot, disabled all but the minimal socially-acceptable push notifications, and moved it to a third home screen folder.
Imagining a First-Party Swift KVO Replacement
My last post got me thinking: if Apple were to add a KVO-like service to Swift, what would it look like? I wish I understood more about how programming languages are created, and how compilers work. What follows isn’t really a serious proposal, but just a sketch of what I would personally want to use as a practicing software developer.
What if every Swift class received an implicit, protected member with a reserved name like observables which provided a means to register closures as observers for changed values? An example usage might look like this:
let tweet = Tweet(text: “Hi.”)
tweet.observables.isLiked.addObserver { (oldValue, newValue) -> Void in
// this is otherwise just a standard closure, with identical
// memory management rules and variable scope semantics.
print(“Value changed.”)
}
tweet.isLiked = true // Console would log “Value changed.”
The observables property would be implied, just like self. It would be an instance of some ad-hoc generated Swift class created by the compiler at compile time. For every observable property of the owning class, this observables class will have a corresponding observable property. So if you created a class like this:
class Tweet {
var isLiked: Bool = false
let text: String
init(text: String) {
self.text = text
}
}
The Swift compiler would generate a class like this and assign it to the observables property for Tweet:
class Tweet_SwiftObservables {
let isLiked = Observable<Bool>()
}
The Tweet_SwiftObservables class name would be auto-generated based on the class name of the target class. Notice that only the isLiked property is carried over, since the text property of Tweet is a let, not a var.
The isLiked member of Tweet_SwiftObservables is an instance of a generic Observable<T> class, whose implementation would be something like the following (though, of course, more nuanced):
class Observable<T> {
typealias Observer = (oldValue: T?, newValue: T?) -> Void
private var observers = [UInt: Observer]()
func addObserver(observer: Observer) -> Uint {
let token: Uint = 0 // generate some unique token
self.observers[token] = observer
return token
}
func removeObserverForToken(token: Uint) {
self.observers[token] = nil
}
}
The money shot is the addObserver() method. This method accepts a single Observer argument, which is just a simple closure. It would follow all the existing rules of memory management and variable scope as any other closure. addObserver() returns a unsigned integer token that can be used to remove the observer at a later time.
What I like about my idea is:
- It’s familiar. It resembles the core mechanic of KVO without the antiquated hassle. It uses existing memory management rules. Everything you already understand about closures applies here.
- It’s type-safe. The
Observable<T>generic class ensures at compile-time that your observers don’t receive an incorrect type. - It’s readable. The syntax is brief without being unclear. Implementing the observation closure at the same call site as
addObserver()keeps cause and effect as close together as possible. - It’s easy. It abandons a stringly-typed API in favor of a compile-time API. Since the
Foo_SwiftObservablesclasses would be auto-generated by the compiler, there’s no need for busywork tasks like keeping redundant manual protocols or keyword constants up to date with the target classes.
NSNotificationCenter is Probably an Anti-Pattern
This week’s iOS Dev Weekly linked to a tweet from Ben Sandofsky about an important change to NSNotificationCenter in iOS 9:
In OS X 10.11 and iOS 9.0 NSNotificationCenter and NSDistributedNotificationCenter will no longer send notifications to registered observers that may be deallocated. If the observer is able to be stored as a zeroing-weak reference the underlying storage will store the observer as a zeroing weak reference, alternatively if the object cannot be stored weakly (i.e. it has a custom retain/release mechanism that would prevent the runtime from being able to store the object weakly) it will store the object as a non-weak zeroing reference. This means that observers are not required to un-register in their deallocation method. Link.
I can’t remember where I heard the following fact (I think it was during one of the WWDC 2015 talks), but it’s my understanding that a deallocated-without-unregistering NSNotificationCenter observer is one of the top causes of crashes in third-party iOS/OS X software. Assuming this is true, a thought struck me today: how much less common would this crash have been if there had been an easier alternative than KVO all this time?
In Foundation, there are only two first-party ways to propagate model changes from one to many observers: KVO and NSNotificationCenter. Both of them are prone to cause crashes when misused, but I’d argue that KVO is harder to use correctly. It’s not unreasonable to imagine that many developers opt for NSNotificationCenter as the least bad option.
It may sound surprising to some that I’d refer to NSNotificationCenter as a “bad” option. I should clarify that I think NSNotificationCenter is a fine API when used for events of a truly global nature. An argument for or against NSNotificationCenter is like an argument for or against a singleton. As I’ve joked on Twitter:
Use a singleton when you need the Sun, not an overhead lamp.
Similarly, I think NSNotificationCenter is best suited for events whose origin is so far outside the scope of local control flow that it would be awkward or even silly to use anything else. The NSNotificationCenter version of my singleton tweet is:
NSTheSunDidRiseNotification, not NSSomebodysPassengerPushedTheFlightAttendantButtonNotification.
In practice I have a hard time justifying the use of NSNotificationCenter for anything besides events vended by the OS, like UIKeyboardWillShowNotification.
I often see NSNotificationCenter used as a clearing house for all the one-to-many relationships between model objects and observers in an app. I think this is probably an anti-pattern. This pattern obligates observers to decide when to ignore notifications.
Consider a Twitter client with a TweetWasLikedNotification. The observers for that notification not only need to know the ID of the liked tweet, but also the local account from which that like action was taken, and/or perhaps the NSManagedObjectContext to which the tweet belongs, etc. Global knowledge belongs at a higher layer in the application. Better still is a design that couples model and observer more closely with an explicit relationship (perhaps a delegate protocol) that doesn’t require such knowledge in the first place.
I have considered using a custom center, instead of the defaultCenter() singleton, but that seems like a half-baked implementation of a design that would be better expressed via a weak-referencing collection of SomeCustomObserverProtocol delegates.
Until Apple provides a Swift-native, first-party way to implement a one-to-many observer pattern, I will continue to use NSObject subclasses and KVO. I’ve been trying to think of ways to abstract out the particulars of NSObject/KVO relationships, keeping the higher-level APIs cleaner and more friendly. Here’s an example of what I mean, posted to GitHub. For a direct-download link of the Xcode playground, click here. I haven’t tried this in production yet, but the idea is to map NSObjects to adapter/observers who produce “view model” structs in response to KVO updates, passing those structs onto their delegates. This lets me use value types instead of the actual model objects for UI updates, without having to abandon KVO as the underlying infrastructure.