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 -transitionFromTo
. 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
and -transitionTo
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 the initializer issue. 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.