January 20, 2015

The Coordinator

One of the biggest problems with bloated view controllers is that they entangle your flow logic, view logic, and business logic.

When a table cell is selected, that delegate method typically looks like this:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    id object = [self.dataSource objectAtIndexPath:indexPath];
    SKDetailViewController *detailViewController = [[SKDetailViewController alloc] initWithDetailObject:object]
    [self.navigationController presentViewController:detailViewController animated:YES];
}

Three simple lines: get the object; create a view controller; present the view controller. In a simple app, this works great. Each view controller is probably used once, and in only one context. Coupling them together like this isn't a dangerous proposition. As your app grows in complexity, however, the same view controller might get used in a new way or in a new place. Maybe it's on the iPad. Maybe it's in an extension. Maybe it's a new flow for doing an old thing.

If the flow logic is encoded into the view controller itself, the view controller can't be reused for its stunningly good looks without dragging all that flow code with it. Don't forget, the view controller base class is prefixed with UI. It's view object, and handling user flow is out of its scope.

In the code example above, the view controller knows how to create the next thing in the flow, and it knows how to present it. Worse still, that flow code is distributed among multiple view controllers, each of which only knows how to perform the next step.

Traditionally, view controllers have been the highest-level component in an app. However, past a certain scale, we’re better served by using even higher-level objects with the authority to boss our view controllers around.

To me, these objects are Coordinators; I've also heard them called Directors. Ideally, you'll have one high-level coordinator that directs the whole app (this is sometimes known as the Application Controller pattern), a coordinator for each navigation controller (especially if you have more than one, as in a tab-based app), and further child coordinators for specific tasks like signing up or creating content. Each coordinator is spawned by its parent coordinator. As always, it can't hurt to start using this pattern early in the development process, so it can even be useful in a single-step task, such as logging in. Single-step tasks grow up to be multi-step.

The Coordinator is a PONSO, like all great objects. For something like Instagram's photo creation flow, we could have a PhotoCreationCoordinator. The app coordinator could spawn a new one, and pass it the root view controller so that it can present the first view controller in the flow.

- (void)beginPhotoCreationProcess {
    PhotoCreationCoordinator *coordinator = [[PhotoCreationCoordinator alloc] initWithRootViewController:self.rootViewController]
    [coordinator beginPhotoCreationProcess];
}

The coordinator's -beginPhotoCreationProcess method can now create a new photo selection view controller and configure it as needed. The coordinator can conform to photo selection view controller's delegate so that it can be informed when it's time to present the next step. None of the view controllers in the process need to know about any of the other view controllers.

Business logic, like posting the photo, is wrapped up in its own objects and can either be pushed up to the coordinator or pushed down to the model, as appropriate. Either way, it comes out of the view controller. Moving it up is great because the coordinator is already acting as the glue between different parts of your code already.

The benefits to extracting the flow into a coordinator are myriad. View controllers now can focus on their goal of binding a model to a view. They can be reused more easily, such as in extensions or unbundled apps, without having complex conditionals to manage different contexts. A/B testing is as simple as creating a different coordinator object and using that to start the process. Flow logic now has a home, and things that have a home feel real nice.

View controller initialization is also extracted. This shouldn't be overlooked. Initializing is always a more complex task than it seems, requiring lots of knowledge about classes and configuations, and we moved it to a better place where more informed decisions can be made.

Coordinator objects naturally fall into one of many disjoint states — in other words (especially for an iPhone app) only one screen is visible at any time. This makes them a ripe place to use state machines to manage all of their configurations.

Apple loves having the view controller be the center of the iOS world. This is obvious in the new UISplitViewController APIs in iOS 8, as well as Interface Builder components like storyboard segues. Unfortunately, a view controller-centric approach to app development isn't scalable. The Coordinator is a great way to isolate your code into small, easily-replaced chunks, and yet another part of the solution to view controller malaise.

January 13, 2015

State Machinery

As awesome as state machines are, they can often be very high-ceremony to set up. A typical Objective-C library requires a lot of code to set up. I wanted to explore combining a few techniques from the this blog to try and make a low-ceremony state machine, one that doesn't require setting up all of your states, transitions, and events ahead of time.

What does the interface for my library look like? Let's take a simple state machine for a subway turnstile:

- (void)init {
    self = [super init];
    if (!self) return nil;

    _stateMachine = [[SKStateMachine alloc] initWithInitialState:@"locked" delegate:self];

    return self;
}

- (void)coinInserted:(id)sender {
    [self.stateMachine transitionToState:@"unlocked"];
}

- (void)humanEntered:(id)sender {
    [self.stateMachine transitionToState:@"locked"];
}

- (BOOL)isLocked {
    return [self.stateMachine canTransitionToState:@"unlocked"];
}

- (void)transitionFromLockedToUnlocked {
    //transition code
}

- (void)transitionFromUnlockedToLocked {
    //transition code
}

That's it. The library uses Dynamic Method Construction to call methods on the delegate, in the form of -transitionFrom<currentState>To<newState>. If a method is defined, it's a valid transition; if not, it throws an exception. You can find the code on Github.

I'd love to add a few things to it, like dynamic methods in the form of -transitionFrom<someState> and -transitionTo<someState> that are always called when entering or leaving a state (unless the transition is invalid). It would also be great to get some userInfo dictionaries passed around to store state, and maybe an optional loggingDelegate. I haven't actually used the library yet, so I don't want to add stuff until I figure out what works and what doesn't.

The other technique I used to write this library is pure objects, which I've written about before on this blog. There are 4 classes in the library: SKStateMachine, SKSelectorConstructor, SKLlamaCaseConverter, and SKComponentSplitter. Each object is pure, requiring nothing but its initializer's parameters and outputting immutable objects. I took the pure object pattern to its logical extreme. Every method in every object takes advantage of an instance variable. Almost every method has no parameters. It's all messages. It's a refreshing and fun way to write code, if a little exhausting. It's readable, well-factored, and truly single-responsiblity.

Objective-C is an old language though, and it's not designed for writing many short objects like these. What are the pain points? There are a few: writing headers is very annoying; writing initializers is very annoying; Objective-C's policy of “no dot syntax unless it's declared as a property” is absurd; the ceremony around lazy-loading is annoying. The good news is that Swift fixes all of these issues except initializer. If I have some read-only properties and an initializer that takes properties with those names, can't the compiler just write the body of that initializer for me? In fact, shouldn't initializers be limited to only that behavior?

When Swift is more mature, it'll be fun to write some of this code in a more modern language. The fundamental point of this project though, constructing selectors and calling them dynamically, isn't possible in Swift. Swift removes message-passing, and thus a lot of dynamic features. I'm curious to see if the simple interface for defining transitions (just define a method!) will make it easier to use state machines in more places.

January 7, 2015

Finite States of America

When writing object-oriented code, it's often much easier to create the objects that store stuff rather than the objects that do stuff. Objects that keep some data and their associated actions are more natural than those that model a process over time. However, we still have to write these workflow objects, since they'll encapsulate a lot of the business logic, flow logic, and complexity of our apps.

What makes writing a workflow object so tough is that it changes over time. This means that when you initialize an object that manages a workflow, you initialize it in an incomplete state. Some of the products of the workflow haven't been calculated yet, by necessity. This means that instance variables start out set to nil, and slowly take values over time. Sometimes, those variables are for values that aren't even products of the workflow! They're just flags, representing how far into the process we are.

How many times have you written code like this?

- (void)performAPIRequest {
    if (self.request) {
        return;
    }

    self.request = [RequestFactory requestWith...
}

Here, self.request serves to assure that we don't perform the API request more that once. It's acting as a cheap, shitty version of state machine. The longer-lived and complex your process is, the worse this becomes. The absolute worst is when this code is the view controller, polluting it with instance variables that have nothing to do with the management of the view.

The solution is simple: upgrade your cheap, shitty state machine to a bona fide, grown-up state machine.

A state machine is a construct that can have several states and defines the transitions between those states that are valid. For example, the Initial state could transition to Loading, which could transition to Completed, Failed, or Cancelled. Other transitions would be invalid.

In the literature, state machines are sometimes referred to as "Finite State Machines", "Finite State Automata", or, my personal favorite jargon, "Deterministic Finite State Automata". This nomenclature is scary to beginners, but it's not a complex tool.

When we define our states, they could be enumerations, but long-time readers of my blog know that I love to use polymorphism instead of enumerations. They give us the benefit of storing the information that each state needs within the state object itself, which I find particularly elegant. A concrete example: if we needed access to the request (for example to cancel it), we could ask the state for it, and only the Loading state actually needs to store that request. All the other states could respond to that message and return nil.

The above object might be reimplemented like so:

- (instancetype)init {
    self = [super init];
    if (!self) return nil;

    self.state = [InitialState new];

    return self;
}

- (void) performAPIRequest {
    if (self.state.isLoadingState) {
        return;
    }
    Request *request = [RequestFactory requestWithSuccessBlock:^{
        self.state = [CompletedState new];
    } failureBlock:((^)(NSError *error)){
        self.state = [[FailureState alloc] initWithError:error];
    }];
    self.state = [[LoadingState alloc] initWithRequest:request];
    [request send];
}

- (void)cancel {
    self.state = [CancelledState new];
}

- (void)setState:(State *)newState {
    [self transitionFromState:self.state toState: newState];
    _state = newState;
}

- (void)transitionFromState:(State *)oldState toState:(State *)newState {
    if (newState.isCancelledState && oldState.isLoadingState) {
        [oldState.request cancel];
    } else if (newState.isCompletedState && oldState.isLoadingState) {
        //inform delegate of completion
    } else {
        [[InvalidTransitionException new] raise];
    }
}

While this is a lot more code for such a simple thing, notice how much more it does than the first example. We've separated the object's path through the states from what the object actually does when transitioning to those states. We could add multiple Failure states for the different ways we can expect this to fail. This explicitness is valuable, since it forces us to think about how our object transforms over time. Note the exception that's raised on an invalid transition. If there's ever a transition that isn't defined, the app will crash and you can easily determine why.

Since you have to define the states yourself, using a state machine is sometimes easier to hand-roll than to rely on a pre-packaged library. Nevertheless, such libraries exist.

The benefits of using state machines are myriad. First, explicitly storing the states and transitions that are valid in your system leaves fewer nooks and crannies for bugs to hide out in. It is said that bugs are just states that you haven't defined yet. If your object is defined by four booleans, it can be in 16 different states, all of which require testing. Imagine how many states it can have with integers, or strings! A state machine limits the number of possible configurations your object can be in, and defines them formally.

Having all of the transitions in one place will help you see the user's flow through the system. What happens if this step takes longer than expected? Clearly-defined transitions make it painfully obvious what happens if an event happens out of order. Since every transition flows through one point, it is trivial to log all of the transitions. You can use that information to determine how your object went off the rails.

Well-named states like these make it easier for new developers to quickly understand your system. Instead of asking "What does self.request mean and why are we checking for its presence?", your new developer can immediately go to asking higher-level questions. State machines don't tend to be used until its too late, but good naming is valuable even if you have only two states.

You'll note that in the only example of architecture that Apple has presented (WWDC 2014 Session 232), they make great use of state machines. You can find the transcipt of that talk here, and the sample code on this page, under "Advanced User Interfaces Using Collection View".

Heraclitus said that you can never step in the same river twice. State machines take this implication and make it obvious in your objects. Start when your class is young, and take advantage of the explicitness of state machines to help you reason about the complex code in your system. It's never too early to make the jump from five poorly-managed instance variables to one beautiful state machine.

December 1, 2014

Pure Objects

Pure functions are functions that don’t cause side effects. Their output is deterministic, meaning that given the same input, they will always return the same output. They also don’t access anything global. For example, if a pure function relied on a random number generator, the seed for that generator would have to be passed in as a parameter. With no external forces acting on them, these functions are easy to test and easy to reason about.

As I write more code, I find that the classes that I enjoy writing the most are those that resemble pure functions. I’ve taken to calling these classes “pure objects”.

What is a pure object? A pure object is initialized with several parameters, and responds only to a few readonly messages. Ideally, it doesn’t access any global state, like singletons. If it does, it allows those to be overridden externally.

The interface for a pure object has three components.

  1. The inputs, which are passed to the initializer. These are the analog to the parameters of a pure function.
  2. Read-only access to the inputs. The purpose of this access is two-fold: first, they let you query the object to understand its inputs; second, they create instance variables to store those inputs. Crucially, these references can’t be changed once they are set in the initializer.
  3. The outputs, which are also represented as read-only properties.

Let’s take a look at an example interface:

@interface SKTimeAgoRenderer

- (instancetype)initWithDate:(NSDate *)date locale:(NSLocale *)locale;

@property (nonatomic, readonly) NSDate *date;
@property (nonatomic, readonly) NSLocale *locale;

@property (nonatomic, readonly) NSString *longTimeAgo; //"2 minutes ago"
@property (nonatomic, readonly) NSString *shortTimeAgo; //"2m"

@end

This specific example can be referred to as decoration. A date object is wrapped in a Renderer, which “decorates” it with extra behavior. Presenters and policies, which I’ve also written about on this blog, are other examples of decoration.

Used up and immediately discarded, pure objects are often short-lived:

[[[SKTimeAgoRenderer alloc] initWithDate:aDate locale:[NSLocale currentLocale]] shortTimeAgo];

The discerning reader might ask, why pass the date into the initializer? We could just as easily make a single TimeAgoRenderer and use it on multiple dates via a message like -shortTimeAgoForDate:.

Passing the date to the initializer makes for a much simpler object. With the multiple-use TimeAgoRenderer, internal methods that needed no parameters before (like -dateInTheLastDay, -dateWasYesterday, and -dateInTheLastYear) would all need the date to be passed to them, a certified code smell.

Further, with the single-use object, self.date never changes, which reduces bugs and increases understandability. The only downside to discarding the object is allocation time, which, at close to 9 nanoseconds, is minimal. Initializing with the date yields simplicity and clarity with little penalty.

In what other ways is our pure object like a pure function? To return more than one result, a pure function takes advantage of a tuple, which is an ordered list of values. Not only can we return multiple values, our outputs are named and can be accessed in any order. (Python uses “named tuples”, but most languages don’t have that feature yet.) Modern functional languages, such as Haskell, take advantage of lazy evaluation. In a similar way, our pure objects can be created in one part of the code, with their outputs uncalculated until they are required.

Pure functions also take advantage of currying. Currying a function lets you pass the initial parameters to a function to create a new function that only needs the final parameters. For example, we can make an addFive by passing only one of the necessary parameters to the add function:

add(5, 3); //8
addFive = add(5); //a curried function that will add 5 to its input
addFive(3); //8

We can do something similar with our pure objects, using convenience initializers. Assuming our normal use will probably pass [NSLocale currentLocale] for the locale, we can create a convenience initializer:

- (instancetype)initWithDate:(NSDate *)date {
    return [self initWithDate:date locale:[NSLocale currentLocale]];
}

This is slightly different than currying, since we have to define the curry ahead of time. It also has an advantage over currying, since we can “curry” the parameters in any order. The big benefit of defining convenience initializers is that we can inject our own parameters (such as the locale) when needed, such as during testing.

To make the usage even easier, we can wrap our entire object in a class method, like [SKTimeAgoRenderer shortTimeAgoForDate:date]. We get the best of both worlds: a short incantation for the most common use case, as well as the ability for verbosity when the situation demands it.

Of course, side-effects are the whole reason we write code; we can’t only write these types of objects. Some of our objects must communicate with the network, write things to databases on disk, and render to the display buffer. Using small objects like these is a great technique to increase expressiveness, concentrating our side-effect-ful code and keeping our responsibilities separate.

November 18, 2014

Wink in All Colors

November 12, 2014

A Controller By Any Other Name

What’s in a name? Isn’t this stuff supposed to be a hard problem anyway? What is a DownloadController, and how is it different from a DownloadQueue?

I love this quote by Graham Lee so much:

Anything that isn’t evidently data or evidently graphics gets put into the amorphous “controller” collection, which eventually sucks your entire codebase into its innards like a black hole collapsing under its own weight.

Charged with bridging between models and views, view controllers grow to become untenable nightmares quickly. It’s become valuable to question what the word “controller” even means to us.

What’s a gesture recognizer? It gets attached to views, suggesting that maybe it belongs in the view layer, but it’s definitely not a view. However, in Smalltalk (the first object-oriented lanugage), user input is classically a controller concern. Which of the two is it? The answer is simple. A gesture recognizer is just that: a gesture recognizer.

Not all objects have to be models, views, and controllers. Some objects are data sources, gateways, routers, and interactions; others still are players, presenters, and operations.

Models and views are easy; they are Things. Representing them in your brain in not a challenge. Controllers are much more amorphous. With models and views, shared behavior in them can be moved up to a superclass, and specific behavior can be moved down to a subclass. All of the models in your app could descend from one class; say, MTLModel. Views could do the same (and in fact, must, if you expect them to play nicely with UIKit). View controllers also have lots of shared behavior and benefit from a supertype. What would a hypothetically-named SKController superclass even do? With responsibilities so broad, what would that interface declaration even be? It would probably be empty.

@interface SKController : NSObject

@end

What useful thing is this object doing? What shared behavior can we give it? And if there’s nothing sensible in SKController, why call things controllers at all? They’re also all objects. It would be just as appropriate to append the word “Object” to every class name.

The harm caused by the “Controller” suffix is subtle, too. When you call something a Controller, it absolves you of the need to separate your concerns. Nothing is out of scope, since its purpose is to control things. Your code quickly devolves into a procedure, reaching deep into other objects to query their state and manipulate them from afar. Boundless, it begins absorbing responsibilities.

The scope of a policy object is very narrow, and it quickly becomes obvious when it starts acting outside of that scope. It has very clearly defined boundaries, and it’s perfectly testable. It is everything a MusicLibraryController is not.

MusicLibraryController can be decomposed into a MusicLibrary with LibraryItems, a Player, and a PlayQueue. We’ve granted a specific role to each component. A new developer needs to know only the names of classes to intuit how they work together. She can do this without reading a single line of code. That’s incredibly powerful.

Naming is hard, but calling things “controller” is easy. (“Manager” is a cop-out too. Sorry.) Spend a little time and think about what role your new object plays in your architecture. Give it an expressive name based on its purpose. If a meaningful name is hard to pin down, it might encapsulate more than one responsibility. Break it up. Read Gang of Four and Patterns of Enterprise Application Architecture from cover to cover. Learn about the different patterns that programmers have been using for decades. Or make up your own! If it feels right, try it out. You’ll know if it’s good.

October 22, 2014

The Myth of the Builder

Software often eludes analogy. It's written in code, compiled into assembly, translated into CPU instructions, run on a kernel, and finally executed on invisibly tiny transistors. Such a complex beast is hard to map onto the real world.

The analogy we've settled on for the creation of software is the architect/builder separation. The designer is the architect: he provides a specification for the product. The programmer is the builder: she implements it. However, this formulation is functionally a myth, and understanding why it's wrong will help explain why software is delivered late, low-quality, and over-budget.

This koan on the Codeless Code is an interesting one. In addition to being cute and snarky, it makes a very strong case that the civil engineer and the software engineer do very different things. We know that the designer is not doing the building; if programmer is also not doing the building, how is software turning from idea into reality?

What is happening is that the designer presents the software as very high-level blueprints to the programmer. The programmer then takes those and creates a lower-level set of blueprints for the compiler.

(Incidentally, the compiler is producing an even lower-level blueprint, called the Intermediate Representation, which is translated in to a final set of blueprints, the specific instructions for different CPUs. It's blueprints all the way down.)

The compiler takes the programmer's blueprints and arranges them (nearly instantly!) into their final form. The actual "construction" phase of software engineering is effectively free. To understand how fundamentally strange that is, imagine a machine that accepts blueprints for a bridge and instantly produces the whole bridge. The completed bridge takes up practically no space so we can produce as many copies of it as we want. In addition, our machine can produce other physical constructs to poke and prod the bridge to make sure it works as expected. That would be a very weird world for civil engineers indeed!

This new analogy helps explain two things about our industry. First, it helps explain why requirements change all the time. People have seen how powerful code is, and how quickly it can be changed. It’s also hard to explain in advance why some changes are much more difficult to make than others, so requirements will change in the process of creating the blueprints. Nothing is final until the "Submit to App Store" button is pressed.

The other thing that this analogy helps explain is the "mythical man month". Bringing on another programmer paradoxically slows the project down. You can add extra builders to a bridge to build it faster, just as you can add extra clock cycles or cores to a CPU to make it compile faster. But nobody has ever suggested that architects are fungible. Smaller teams produce higher quality code for this reason. The "architects" aren't isolated, given a rote task, and managed in a top-down fashion. Designing software architecture takes omnidirectional communication, which gets costly as teams grow.

What else can we learn here? Because the designer's "blueprints" are interpreted by a human, any inconsistencies can be smoothed out. The programmer's blueprints have to be much more detailed, since they will be interpreted literally and unambiguously by a CPU. It pays to have more explicit blueprints earlier in the pipeline, because the programmer will have to do less interpolation to fill those gaps. Creating those specifications will result in better software, and can help with reducing requirement creep.

Because computers only emulate the real world for convenience's sake, it can be hard to remember how many layers of abstraction lie between the code you write and the actual silicon that runs it. In Structure and Interpretation of Computer Programs, Abelson and Sussman say:

A computational process is indeed much like a sorcerer’s idea of a spirit. It cannot be seen or touched. It is not composed of matter at all. However, it is very real. It can perform intellectual work. It can answer questions. It can affect the world by disbursing money at a bank or by controlling a robot arm in a factory. The programs we use to conjure processes are like a sorcerer’s spells.

It's not valuable to think of software as a bridge; it's too weird for that.

September 30, 2014

8 Patterns to Help You Destroy Massive View Controller

View controllers become gargantuan because they’re doing too many things. Keyboard management, user input, data transformation, view allocation — which of these is really the purview of the view controller? Which should be delegated to other objects? In this post, we’ll explore isolating each of these responsiblities into its own object. This will help us sequester bits of complex code, and make our code more readable.

In a view controller, these responsibilities might be grouped into #pragma mark sections. When that happens, it’s usually time to start thinking about breaking it apart into smaller components.

Data Source

The Data Source Pattern is a way of isolating the logic around which objects live behind what index paths. Particularly in complicated table views, it can be useful to remove all of the logic of “Which cells are visible under these conditions?” from your view controller. If you’ve ever written a table view where you’re constantly comparing integers of rows and sections, a data source object is for you.

Data source objects can literally conform to the UITableViewDataSource protocol, but I’ve found that configuring cells with those objects is a different role than managing index paths, so I like to keep these two objects separate.

A simple example of a data source might handle sectioning logic for you.

@implementation SKSectionedDataSource : NSObject

- (instancetype)initWithObjects:(NSArray *)objects sectioningKey:(NSString *)sectioningKey {
    self = [super init];
    if (!self) return nil;

    [self sectionObjects:objects withKey:sectioningKey];

    return self;
}

- (void)sectionObjects:(NSArray *)objects withKey:(NSString *)sectioningKey {
    self.sectionedObjects = //section the objects array
}

- (NSUInteger)numberOfSections {
    return self.sectionedObjects.count;
}

- (NSUInteger)numberOfObjectsInSection:(NSUInteger)section {
    return [self.sectionedObjects[section] count];
}

- (id)objectAtIndexPath:(NSIndexPath *)indexPath {
    return self.sectionedObjects[indexPath.section][indexPath.row];
}

@end

While this data source is designed to be abstract and reusable, don’t be afraid to make a data source that’s used in only one place in your app. Separating index path management logic from your view controller is a noble goal in itself. Especially for highly dynamic table views, it’s good to have an object that notifies the view controller with messages like “Hey, I have a new object at this index path”; the view controller can then pass that along to the table view in the form of an animation.

This pattern can also encapsulate your retrieval logic. A remote data source can fetch a collection of objects from an API. UIViewController is a UI object, and this is a great way to get networking code out of your view controller.

If the interface to all your data sources is stable (with a protocol for example), you can write a special data source that is composed of an arbitrary number of other data sources. Each sub-data-source becomes its own section in the multi data source. Using this logic to combine data sources is a great way to avoid having to write dreadful index comparison code. The data source will manage it all for you!

Standard Composition

View controllers can be composed using the View Controller Containment APIs introduced in iOS 5. If your view controller is composed of several logical units that could each be their own view controller, consider using Composition to break them apart. A practical application of this litmus test is a screen with multiple table views or collection views.

On a screen with a header and a grid view, we could lazy load our two view controllers, and lay them out properly when prompted by the system.

- (SKHeaderViewController *)headerViewController {
    if (!_headerViewController) {
        SKHeaderViewController *headerViewController = [[SKHeaderViewController alloc] init];

        [self addChildViewController:headerViewController];
        [headerViewController didMoveToParentViewController:self];

        [self.view addSubview:headerViewController.view];

        self.headerViewController = headerViewController;
    }
    return _headerViewController;
}

- (SKGridViewController *)gridViewController {
    if (!_gridViewController) {
        SKGridViewController *gridViewController = [[SKGridViewController alloc] init];

        [self addChildViewController:gridViewController];
        [gridViewController didMoveToParentViewController:self];

        [self.view addSubview:gridViewController.view];

        self.gridViewController = gridViewController;
    }
    return _gridViewController;
}

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    CGRect workingRect = self.view.bounds;

    CGRect headerRect = CGRectZero, gridRect = CGRectZero;
    CGRectDivide(workingRect, &headerRect, &gridRect, 44, CGRectMinYEdge);

    self.headerViewController.view.frame = tagHeaderRect;
    self.gridViewController.view.frame = hotSongsGridRect;
}

The resulting sub-view controllers, each with their own collection view, handle one uniform type of data. They are much smaller and easier to understand and change because of it.

Smarter Views

If you’re allocating all of your view controller’s subviews inside of the view controller’s class, you may consider using a Smarter View. UIViewController defaults to using UIView for it’s view property, but you can override it with your own view. You can use -loadView as the access point for this, as long as you set self.view in that method.

@implementation SKProfileViewController

- (void)loadView {
    self.view = [SKProfileView new];
}

//...

@end

@implementation SKProfileView : NSObject

- (UILabel *)nameLabel {
    if (!_nameLabel) {
        UILabel *nameLabel = [UILabel new];
        //configure font, color, etc
        [self addSubview:nameLabel];
        self.nameLabel = nameLabel;
    }
    return _nameLabel;
}

- (UIImageView *)avatarImageView {
    if (!_avatarImageView) {
        UIImageView * avatarImageView = [UIImageView new];
        [self addSubview:avatarImageView];
        self.avatarImageView = avatarImageView;
    }
    return _avatarImageView
}

- (void)layoutSubviews {
    //perform layout
}

@end

You can simply redeclare @property (nonatomic) SKProfileView *view, and because it is a more specific type than UIView, the analyzer will do the right thing and assume self.view is an SKProfileView. This is called a covariant return types and is very useful for this pattern. (The compiler needs to know that your class is a subclass of UIView, so make sure you import the .h file instead of doing an @class forward declaration!)

Presenter

The Presenter Pattern wraps a model object, transforms its properties for display, and exposes messages for those transformed properties. It is also known in other contexts as Presentation Model, the Exhibit pattern, and ViewModel.

@implementation SKUserPresenter : NSObject

- (instancetype)initWithUser:(SKUser *)user {
    self = [super init];
    if (!self) return nil;
    _user = user;
    return self;
}

- (NSString *)name {
    return self.user.name;
}

- (NSString *)followerCountString {
    if (self.user.followerCount == 0) {
        return @"";
    }
    return [NSString stringWithFormat:@"%@ followers", [NSNumberFormatter localizedStringFromNumber:@(_user.followerCount) numberStyle:NSNumberFormatterDecimalStyle]];
}

- (NSString *)followersString {
    NSMutableString *followersString = [@"Followed by " mutableCopy];
    [followersString appendString:[self.class.arrayFormatter stringFromArray:[self.user.topFollowers valueForKey:@"name"]];
    return followersString;
}

+ (TTTArrayFormatter*) arrayFormatter {
    static TTTArrayFormatter *_arrayFormatter;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _arrayFormatter = [[TTTArrayFormatter alloc] init];
        _arrayFormatter.usesAbbreviatedConjunction = YES;
    });
    return _arrayFormatter;
}

@end

Crucially, the model object itself is not expoosed. The Presenter serves as the gatekeeper to the model. This ensures that the view controller can’t sidestep the presenter and directly access the model. Architecture like this limits your dependency graph, and since the SKUser model touches fewer classes, changing it will causes fewer effects in your app.

Binding pattern

In method form, this might be called -configureView. The Binding Pattern updates a view with model data as it changes. Cocoa is a natural place to use this because KVO can observe the model, and KVC can read from the model and “write” to the view. Cocoa Bindings are the AppKit version of this pattern. Third-party libraries like Reactive Cocoa are also effective for this pattern, but may be overkill.

This pattern works really well in conjunction with the Presenter Pattern, using one object to transform values, and another to apply them to your view.

@implementation SKProfileBinding : NSObject

- (instancetype)initWithView:(SKProfileView *)view presenter:(SKUserPresenter *)presenter {
    self = [super init];
    if (!self) return nil;
    _view = view;
    _presenter = presenter;
    return self;
}

- (NSDictionary *)bindings {
    return @{
              @"name": @"nameLabel.text",
              @"followerCountString": @"followerCountLabel.text",
            };
}

- (void)updateView {
    [self.bindings enumerateKeysAndObjectsUsingBlock:^(id presenterKeyPath, id viewKeyPath, BOOL *stop) {
        id newValue = [self.presenter valueForKeyPath:presenterKeyPath];
        [self.view setObject:newvalue forKeyPath:viewKeyPath];
    }];
}

@end

(Note that our simple presenter from above isn’t necessarily KVO-able, but it could be made to be so.)

As with all of these patterns, you don’t have to figure out the perfect abstraction on your first attempt. Don’t be afraid to make an object that is only usable in one specific case. The goal is not to eliminate code reuse, it’s to simplify our classes so that they’re easier to maneuver through and understand.

Interaction pattern

The ease of typing actionSheet.delegate = self is part of the reason that view controllers become too big to fail. In Smaltalk, the entire role of the Controller object was for accepting user input and updating the views and models. Interactions today are more complex, and they cause bulky code in the view controller.

Interactions often include an initial user input (like a button press), optional additional user input (“Are you sure you want to X?”), and then some activity, like a network request or state change. The entire lifecycle of that operation can be wrapped up inside the Interaction Object. The example below creates the interaction object when the button is tapped, but adding the Interaction Object as the target of an action, like [button addTarget:self.followUserInteraction action:@selector(follow)] is also good.

@implementation SKProfileViewController

- (void)followButtonTapped:(id)sender {
    self.followUserInteraction = [[SKFollowUserInteraction alloc] initWithUserToFollow:self.user delegate:self];
    [self.followUserInteraction follow];
}

- (void)interactionCompleted:(SKFollowUserInteraction *)interaction {
    [self.binding updateView];
}

//...

@end

@implementation SKFollowUserInteraction : NSObject <UIAlertViewDelegate>

- (instancetype)initWithUserToFollow:user delegate:(id<InteractionDelegate>)delegate {
    self = [super init];
    if !(self) return nil;
    _user = user;
    _delegate = delegate;
    return self;
}

- (void)follow {
    [[[UIAlertView alloc] initWithTitle:nil
                                message:@"Are you sure you want to follow this user?"
                               delegate:self
                      cancelButtonTitle:@"Cancel"
                      otherButtonTitles:@"Follow", nil] show];
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    if  ([alertView buttonTitleAtIndex:buttonIndex] isEqual:@"Follow"]) {
        [self.user.APIGateway followWithCompletionBlock:^{
            [self.delegate interactionCompleted:self];
        }];
    }
}

@end

The old style of alert view and action sheet delegates make this pattern more apparent, but it works just as well with the new iOS 8 UIAlertController APIs.

Keyboard Manager

Updating the view after the keyboard state changes is another concern that is classically stuck in the view controller, but this responsibility can easily be shifted in a Keyboard Manager. There are implementations of this pattern designed to work in every case, but again, if that’s overkill, don’t be afraid to write your own quick version of this.

@implementation SKNewPostKeyboardManager : NSObject

- (instancetype)initWithTableView:(UITableView *)tableView {
    self = [super init];
    if (!self) return nil;
    _tableView = tableView;
    return self;
}

- (void)beginObservingKeyboard {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
}

- (void)endObservingKeyboard {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
}

- (void)keyboardWillShow:(NSNotification *)note {
    CGRect keyboardRect = [[note.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];

    UIEdgeInsets contentInsets = UIEdgeInsetsMake(self.tableView.contentInset.top, 0.0f, CGRectGetHeight(keyboardRect), 0.0f);
    self.tableView.contentInset = contentInsets;
    self.tableView.scrollIndicatorInsets = contentInsets;
}

- (void)keyboardDidHide:(NSNotification *)note {
    UIEdgeInsets contentInset = UIEdgeInsetsMake(self.tableView.contentInset.top, 0.0f, self.oldBottomContentInset, 0.0f);
    self.tableView.contentInset = contentInset;
    self.tableView.scrollIndicatorInsets = contentInset;
}

@end

You can call -beginObservingKeyboard and -endObservingKeyboard from -viewDidAppear and -viewWillDisappear or wherever’s appropriate.

Navigator

Navigating from screen to screen is normally done with a call to -pushViewController:animated:. As these transitions get more complicated, you can delegate this task to a Navigator object. Especially in a universal iPhone/iPad app, navigation needs to change depending on what size class your app is currently running in.

@protocol SKUserNavigator <NSObject>

- (void)navigateToFollowersForUser:(SKUser *)user;

@end

@implementation SKiPhoneUserNavigator : NSObject<SKUserNavigator>

- (instancetype)initWithNavigationController:(UINavigationController *)navigationController {
    self = [super init];
    if (!self) return nil;
    _navigationController = navigationController;
    return self;
}

- (void)navigateToFollowersForUser:(SKUser *)user {
    SKFollowerListViewController *followerList = [[SKFollowerListViewController alloc] initWithUser:user];
    [self.navigationController pushViewController:followerList animated:YES];
}

@end

@implementation SKiPadUserNavigator : NSObject<SKUserNavigator>

- (instancetype)initWithUserViewController:(SKUserViewController *)userViewController {
    self = [super init];
    if (!self) return nil;
    _userViewController = userViewController;
    return self;
}

- (void)navigateToFollowersForUser:(SKUser *)user {
    SKFollowerListViewController *followerList = [[SKFollowerListViewController alloc] initWithUser:user];
    self.userViewController.supplementalViewController = followerList;
}

This highlights one of the benefits to using lots of small objects instead of one big object. They can be changed, rewritten, and replaced very quickly. Instead of shameful conditional code all over your view controller, you can just set self.navigator to [SKiPadUserNavigator new] when on the iPad, and it will respond to the same -navigateToFollowersForUser: method. Tell, don’t ask!

Wrapping up

Historically, Apple’s SDKs only contain the bare minimum of components, and those APIs push you towards Massive View Controller. By tracking down the responsibilities of your view controllers, separating the abstractions out, and creating true single-responsibility objects, we can begin to reign those gnarly classes in and make them managable again.

September 19, 2014

Anatomy of a Feature: Push Notifications

Sites like Stack Overflow naturally fall into the groove of giving quick answers to common problems. Take, for example, this question, with answers containing code snippets removed from any method, or worse, answers with code injected right into the app delegate. This code shows you how to do the thing you’re trying to do, but doesn’t show you the bigger picture. Where does this stuff go? How does it talk to other components?

In this post, I hope to describe how a common subsystem of an app, such as push notifications, might be architected. It’s an object-oriented architecture, and we will talk about interfaces more and implementations less.

Our requirements are pretty straightforward:

  • Handle token registration
  • Forward push notifications recieved while backgrounded to a router object, so that the app can configure its screens properly
  • Show a toast notification for notifications received while the app is foregrounded
  • Allow any object (such as a conversation view controller) to supress the toast notification
  • Allow any object to be informed when a push notification is received, so it can execute any releveant code, such as animating in new messages

The App Delegate

Ah, my old nemesis, the app delegate. Apple uses this pattern as a central point to get information into our app. Notably, methods like - (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken encourage developers to put the code right into the app delegate, quickly bloating it. We should dispatch to another object immediately. This will separate our intent from our implementation, and make the resulting code easier to read.

The Push Center

We need a central object to store state (such as which objects want to be able to suppress notifications) and to make decisions about how to handle push notifications. That object, RGPushCenter, will be a singleton. Singletons are bad, and if our object were only accessed from the app delegate, it could easily just be a property on the app delegate. However, it also needs to be accessed from elsewhere in the app (other objects need to tell it to suppress notifications), so global access is required.

There are a few things we can do to mitigate the damage a singleton can do. Primarily, our singleton will have only one point of access, which is the +sharedCenter class method. Having lots of class methods makes it hard to have state in your object, which makes it hard to refactor.

The push center is the home for code like push notification registration. If you’re tempted to just leave this code (it’s only one line!) in the app delegate, consider the complexity that might be added. For example, while I was writing this code, I learned that iOS 8 handles push notification registration differently. Because the code was hidden in the push center, I was able to easily change it, leaving the app delegate none the wiser.

- (void)registerForRemoteNotifications {
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
    {
        [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
        [[UIApplication sharedApplication] registerForRemoteNotifications];
    }
    else
#endif
    {
        UIRemoteNotificationType types = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert;
        [[UIApplication sharedApplication] registerForRemoteNotificationTypes:types];
    }
}

This way, the code behaves correctly for iOS 7 and 8, in Xcodes 5 and 6. Adding this to the app delegate would only serve to confuse the reader (“What is this #define for?”, “What happened in iOS 8?”, etc). Hiding things at the right level of abstraction makes it easier to understand then code when you read it later.

We should always be dispatching immediately to our push center object, like so:

- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
    [[RGPushCenter sharedCenter] didRegisterForRemoteNotificationsWithToken:deviceToken];
}

Inside this method, we have to convert our NSData object to an NSString, and then register it to the API. You might be tempted to use a C function to convert the data to a string, but let’s initialize a special object for it.

The Push Token

RGPushToken *pushToken = [[RGPushToken alloc] initWithTokenData:token];

It might seem like overkill to allocate memory for a whole object for our push token, but allocation is pretty cheap, all things considered. Further, once we have an object, it’s very easy to add a simple message like [pushToken registerWithAPI] to perform the API call to the server. The RGPushToken class now nicely encapsulates the NSData to NSString conversion as well as the network request, leaving the push center dumber and better off for it.

Inside the push token class, we can make a one line call to our HTTP session manager:

- (void)registerWithGeniusAPI {
    [[RGAPI manager] POST:@"/account/devices" parameters:self.POSTParameters success:nil failure:nil];
}

Because this is its own class, the -POSTparameters can be wrapped up into their own method as well. At each layer of abstraction, we push down the complexity into another abstraction until the thing is so simple that it can be understood by glancing at it.

The Push Notification

In some cases, we don’t want to dispatch immediately to the push center, but make a decision first. When receiving a push notification when the app is running, we need to make a decision based on whether or not it’s backgrounded.

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
    if (application.applicationState == UIApplicationStateActive) {
        [[RGPushCenter sharedCenter] presentToastWithUserInfo:userInfo];
    } else {
        [[RGPushCenter sharedCenter] performRoutingForNotificationWithUserInfo:userInfo];
    }
}

This is the right place for this decision for a number of reasons. We don’t want the push center to have to know about different ways that the same data comes in; in fact, if it were up to me, this would probably be two delegate methods. We also already have access to the application object, and we can quickly query it’s state to send more intelligible messages to the push center, leaving it much cleaner.

What do we do with the notification dictionary once it comes into our push center? If you guessed “make an object!”, you’d be correct.

RGPushNotification *notification = [[RGPushNotification alloc] initWithDictionary:userInfo];

Object-oriented programming is all about encapsulation. What are we encapsulating here? Whether or not the push notification is silent, the type of notification (“new message!”, “update the unread count!”), and where the app should go when the notification is activated, which is stored in the routing URL. This also gives us the flexiblity to transform or expose more as needed in the future, and localize those changes to only this class. Once we have our notification object, we can grab its routing URL and pass it a router object that handles configuring our view controllers:

[[RGNavigationManager sharedManager] handleOpenURL:notification.routingURL];

This code could access the raw data from the userInfo dictionary, but we’d end up with stringly-typed NSURL conversion in our push center. [NSURL urlWithString:userInfo[@"genius"][@"url"]] is a lot less elegant and truthfully, we just don’t care where the URL string is or how it’s converted into an NSURL. Offload that responsibility to another class!

The Delegates

The only complex component remaining is the preventing the toast from being shown if any other objects wish to suppress it. We need to be able to ask other objects that care whether they want to prevent the toast from happening, but we don’t want to be too tightly coupled to the other objects. Of the common communication patterns, most of them (NSNotifications, KVO, target-action) can’t return data. Blocks are an interesting solution, but would require some kind of complex token-based system to deregister a block, so we will use delegates for simplicity.

Having just one delegate can be dangerous with a singleton, since another object can be take away your delegateness at any point without telling you. For safety’s sake, let’s keep multiple delegates. Order doesn’t matter, so we can use a set-like construct, but we want to it to hold a weak (not strong or unsafe) reference to each of the items in it. Forunately, the Foundation SDK gives us [NSHashTable weakObjectsHashTable], which is essentially a set with weak references, exactly what we’re interested in.

From there, we need a way to register and deregister objects as delegates.

- (void)addDelegate:(id<RGPushCenterDelegate>)delegate;
- (void)removeDelegate:(id<RGPushCenterDelegate>)delegate;

We can now write a method to check if any of the delegates want to suppress the notification.

- (BOOL)shouldSuppressNotification:(RGPushNotification *)notification {
    for (id<RGPushCenterDelegate> delegate in self.delegates) {
        if ([delegate pushCenter:self shouldSuppressNotification:notification]) {
            return YES;
        }
    }
    return NO;
}

Finally, we’ll create an NSNotification called RGPushNotificationWasReceived, which observers can listen to and perform their own actions. We already have delegates, why include a separate communication pattern here? The reason is simple. We want to keep the concept of suppressing a notification separate and decoupled from the concept of listening for notifications. Some objects will just need to know that a notification is recieved (such as the conversation list, which needs to update, but not suppress the toast). Notifications also don’t really require any knowledge of the push center class, only the push notification object, which is another reason to keep them separate.

This writeup doesn’t provide code that you can copy and paste into your own project, but it’s not particularly complex either (the actual push center is only 130 lines of code). I hope that it provides you with a framework for thinking about how to architect features like this in future.