Design Patterns: Locks and Keys

January 26, 2019

John Sundell’s Locks and Keys design pattern (also covered in this talk) is worth a look. In short, rather than having a manager class with, say, var currentUser: User?, you create a specialized factory class that represents the state of having a user (var currentUser: User), which is in turn used to create the appropriate view controllers with the user populated/injected.

For the past few years I’ve considered the Coordinator pattern (popularized on iOS by Soroush Khanlou) to be my go-to high level app architectural pattern. For me, Locks and Keys closely resemble how the Coordinator pattern has worked out in practice, particularly after several years of Swift. As an example, a root AppCoordinator might create and defer to LoggedInCoordinator and LoggedOutCoordinator, depending on the state of the app. These coordinators then have private methods to create populated view controllers.

Recently I spent some time experimenting with Flutter, writing an app that shows a “Connect to Server” screen prompting for address and port when not connected, and the rest of the user interface when it is connected. Flutter seems to encourage a similar pattern to the above: if the app is not connected to the server, build() returns a widget for the “Connect to Server” screen. If it is connected, a different widget is built, with the reference to the connection passed to its constructor.

A few observations of differences and similarities between Swift/UIKit and Dart/Flutter here:

  • Swift’s optionals must be dealt with. Without the above pattern, as Sundell points out, you end up with a lot of guard-assertionFailure clutter – error handling code you never expect to be executed.
  • In Dart, any reference can be null, but there’s also no need to unwrap them. As a result I imagine most code only rarely checks for null, resulting in the occasional “Oh, I forgot to pass that in…” crash.
  • If you use UIKit storyboards to load your view controllers, you can’t override the initializer and must instead rely on a convention of populating the injected dependencies, models, etc. One could argue that this is roughly equivalent to pitfalls of Dart’s “everything is optional”. (This issue with UIKit view controller instantiation is well understood by the community.)
  • Flutter’s design encourages passing dependencies down – stark comparison to UIKit storyboards.

Check out John Sundell and Soroush Khanlou’s posts if you haven’t already.

Comments
Tags: ,

The Next Chapter

September 2, 2015

I’ve just begun work at Ritual Development Group, Inc.. Ritual is a product-oriented software company. I’m serving as the CTO and will share more about what we’re building as time goes on. For now, suffice it to say that this is a huge step for me; It is the realization of a dream I’ve had for quite some time.

As wonderful as this is, it has meant making the very difficult decision to leave Big Nerd Ranch, where I have proudly worked for the past five years. I joined BNR in 2010, just as the original iPad was being released. Since that time, BNR has experienced tremendous growth.

At BNR I worked as an independent engineer, a team lead, an author, a teacher, and as the VP of Engineering. I was able to work with startup companies, government contractors, and billion-dollar multinational conglomerates. I feel fortunate to have learned a tremendous amount in that time, and quickly.

Teaching was one of the best parts of my time at BNR. As much as I love writing software, it’s really hard to beat a week spent teaching a group of nerds about platforms you love, especially when every student is excited to be there. The days for an instructor are long, but it’s a truly rewarding experience, even (or especially) when you’re sitting in the lab at 10:00 PM sketching out an app architecture with a student. BNR’s bootcamps are an exceptional experience. If you are serious about advancing as a software developer, I wholeheartedly recommend taking a class.

Co-authoring the fourth and fifth editions of Cocoa Programming for OS X was a remarkable experience. I couldn’t be more thankful to Aaron for the trust he placed in me. I am especially proud of the fifth edition, which my friend and colleague Nate co-authored with me. While the fourth edition was an important update, but the fifth edition was truly an overhaul, a major version release with substantial refactoring. We focused on demonstrating modern app architecture and style while also transitioning from Objective-C to Swift.

All of my prior work has stretched me, but the opportunity to work as the VP of Engineering stretched me in a whole new dimension (or two). Looking back on it, I believe we accomplished a lot. But it’s also challenging to fully take stock of it. I owe many thanks to my team of engineering directors: Brandy, Brian, the Chrises, and Steven. Working with them and all of the Nerds was a truly rewarding experience. It’s hard to part ways with such a fine group of people.

Looking back over my time at BNR, it’s almost unreal how much I grew there, even in some of the simplest ways. I’m a fairly quiet person and the fact that I can happily deliver a talk on a chapter from the Cocoa book feels very incongruous. I also drink coffee now. I owe a great deal to all of the people at BNR who gave me so many chances to grow over the years.

So here we are. This chapter has come to a close. I’m grateful for all that’s past, and I look with great anticipation to what’s next.

Comments

Handling Keyboard Input in a View Controller – Alongside SpriteKit

April 15, 2015

I’m using SpriteKit in the train game I’ve been tinkering with on and off, and recently went to add keyboard event handling to my view controller:

override func keyDown(event: NSEvent) {
    interpretKeyEvents([event])
}

override func deleteBackward(sender: AnyObject?) {
    println("deleteBackward")
}

To my surprise, though, whatever was first responder wasn’t passing keyDown: up the responder chain. (This is 10.10 so view controllers are in the responder chain automatically.)

First, determining what was first responder was easy enough. After showWindow: had been called:

(lldb) ex windowController.window!.firstResponder
(SKView) $R2 = 0x0000000100e1b100 {

The SKView is the first responder. I had forgotten that SKNode is a subclass of NSResponder and that nodes can participate in the responder chain.

So why wasn’t the keyDown: propagating up to the view controller? I checked the nextResponder of the SKView: it was the same as its superview, which is to be expected. The question remains: why wasn’t keyDown: propagating?

For the answer to that I turned to Hopper Disassembler, which revealed that -[SKView keyDown:] essentially sends keyDown: to its scene:

void -[SKView keyDown:](void * self, void * _cmd, void * arg2) {
    rbx = self;
    r14 = [arg2 retain];
    if (rbx->_disableInput == 0x0) {
        rdi = rbx->_scene;
        if (rdi != 0x0) {
            [rdi keyDown:r14];
        }
    }
    rdi = r14;
    rax = [rdi release];
    return;
}

So how can I deal with this? I could subclass SKView and override keyDown: to give it a more traditional implementation (pass it on to nextResponder, for example). However, while I was in Hopper I noticed that SKScene doesn’t implement keyDown:, which means that it is relying on the default implementation of keyDown: in NSResponder. Which means that the simplest solution is to set the scene’s next responder to my view controller:

let gameScene = GameScene(size: ...)
skView.presentScene(gameScene)

assert(gameScene.nextResponder == nil)
gameScene.nextResponder = self

Now my view controller receives keyboard events: problem solved! My guess is that SpriteKit’s designers intended for the keyboard event handling to be done in an SKScene subclass – that’s how it’s done in the Adventure sample code. Unfortunately that doesn’t make sense for my app architecture, where there’s more to the UI than an SKView.

Comments
Tags: ,