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.
Steven Sinofsky on Why Remote Engineering is So Difficult
I have spent a lot of time trying to manage work so it is successful outside of a single location. I’ve had mixed results and have found only three patterns which are described below.
…
The first pattern is good to know, just not scalable or readily reproducible. That is when you have a co-located and functioning team and members need to move away for some reason then remote work can continue pretty much as it has before. This assumes that the nature of the work, the code, the project all continue on a pretty similar path.
…
The second pattern that works is when a project is based on externally defined architectural boundaries. In this case little knowledge is required that span the seam between components. What I mean by externally defined is that the API between the major pieces, separated by geography, is immutable and not defined by the team.
…
The third pattern that works is that those working remotely have projects that have essentially no short term or long term connection to each other. This is pretty counter-intuitive. It is also why startups are often the first places to see remote work as challenging, simply because most startups only work on things that are connected.
The entire article is worth your time if you’re thinking about building distributed teams.