Latest 0.9.0
License MIT
Platforms ios 6.0, watchos 2.0, tvos 9.0, osx 10.8, requires ARC

Build Status

A lightweight hierarchical state machine framework in Objective-C.


  • This is a fork from TBStateMachine.
  • DLStateMachine wraps the NSOpearationQueue with an TBExecutor to allow synchron and asynchronous execution.
  • The synchron execution will be helpful if you need to include the State Machine in your unit tests.


  • Block based API
  • Nested states
  • Orthogonal regions
  • Pseudo states (fork, join and junction)
  • External, internal and local transitions with guards and actions
  • State switching using least common ancestor algorithm (LCA)
  • Thread safe event handling
  • Asynchronous event handling
  • NSNotificationCenter support



  • watchOS 2.0
  • tvOS 9.0
  • iOS 6.0
  • OS X 10.8


DLStateMachine is available through CocoaPods. To install
it, simply add the following line to your Podfile:

pod 'DLStateMachine'



#import <TBStateMachine/TBSMStateMachine.h>

Create a state, set enter and exit blocks:

TBSMState *stateA = [TBSMState stateWithName:@"StateA"];
stateA.enterBlock = ^(id data) {


stateA.exitBlock = ^(id data) {


Create a state machine:

TBSMStateMachine *stateMachine = [TBSMStateMachine stateMachineWithName:@"Main"];

Add states and set state machine up. The state machine will always set the first state in the given array as the initial state unless you set the initial state explicitly:

stateMachine.states = @[stateA, stateB, ...];
stateMachine.initialState = stateB;
[stateMachine setUp:nil];

Event Handling

You can add event handlers which trigger transitions to specified target states:

[stateA addHandlerForEvent:@"EventA" target:stateB];

You can also add event handlers with additional action and guard blocks:

TBSMActionBlock action = ^(id data) {


TBSMGuardBlock guard = ^BOOL(id data) {

    return YES;

[stateA addHandlerForEvent:@"EventA" target:stateB kind:TBSMTransitionExternal action:action guard:guard];

If you register multiple handlers for the same event the guard blocks decide which transition will be fired.

Different Kinds of Transitions

By default transitions are external. To define a transition kind explicitly choose one of the three kind attributes:


Scheduling Events

To schedule the event call scheduleEvent: and pass the specified TBSMEvent instance and (optionally) an object as payload:

TBSMEvent *event = [TBSMEvent eventWithName:@"EventA" data:aPayloadObject];
[stateMachine scheduleEvent:event];

The payload will be available in all action, guard, enter and exit blocks which are executed until the event is successfully handled.


Event processing follows the Run-to-Completion model to ensure that only one event will be handled at a time. A single RTC-step encapsulates the whole logic from evaluating the event to performing the transition to executing guards, actions, exit and enter blocks.

Events will be queued and processed one after the other.

Nested States

TBSMState instances can also be nested by using TBSMSubState:

TBSMSubState *subState = [TBSMSubState subStateWithName:@"SubState"];
substate.stateMachine = subMachine;

stateMachine.states = @[stateA, stateB, subState];

You can also register events, add enter and exit blocks on TBSMSubState, since it is a subtype of TBSMState.

Orthogonal Regions

To build orthogonal regions you will use TBSMParallelState:

TBSMParallelState *parallel = [TBSMParallelState parallelStateWithName:@"ParallelState"];
parallel.stateMachines = @[subMachineA, subMachineB, subMachineC];

stateMachine.states = @[stateA, stateB, parallel];

Pseudo States

TBStateMachine supports fork and join pseudo states to construct compound transitions:


TBSMFork *fork = [TBSMFork forkWithName:@"fork"];
[stateA addHandlerForEvent:@"EventA" target:fork];
[fork setTargetStates:@[stateB, stateC] inRegion:parallel];


TBSMJoin *join = [TBSMJoin joinWithName:@"join"];
[stateA addHandlerForEvent:@"EventA" target:join];
[stateB addHandlerForEvent:@"EventB" target:join];
[join setSourceStates:@[stateA, stateB] inRegion:parallel target:stateC];


TBSMJunction *junction = [TBSMJunction junctionWithName:@"junction"];
[stateA addHandlerForEvent:@"EventA" target:junction];
[junction addOutgoingPathWithTarget:stateB action:nil guard:^BOOL(id data) {

    return // ...
[junction addOutgoingPathWithTarget:stateC action:nil guard:^BOOL(id data) {

    return // ...


TBSMState posts an NSNotification on entry and exit:

  • TBSMStateDidEnterNotification
  • TBSMStateDidExitNotification

The notification’s userInfo contains:


To receive a notification:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(myHandler:) name:TBSMStateDidEnterNotification object:stateA];

- (void)myHandler:(NSNotification *)notification
    id myPayloadObject = notification.userInfo[TBSMDataUserInfo];

TBSMState also posts an NSNotification with the event name when an internal transition has been performed:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(myHandler:) name:@"EventA" object:stateA];

- (void)myHandler:(NSNotification *)notification
    id myPayloadObject = notification.userInfo[TBSMDataUserInfo];

Thread Safety and Concurrency

TBStateMachine is thread safe. Each event is processed asynchronously on the main queue by default. This makes handling of UIKit components convenient.

To use a dedicated background queue simply set:

NSOperationQueue *queue = [NSOperationQueue new]; = @"com.myproject.queue";
queue.maxConcurrentOperationCount = 1;
stateMachine.scheduledEventsQueue = queue;

Debug Support

DLStateMachine offers debug support through the subspec DebugSupport. Simply add it to your Podfile (most likely to a beta target to keep it out of production code):

target 'MyBetaApp', :exclusive => true do
  pod 'DLStateMachine/DebugSupport'

Then include TBSMDebugger.h to the debug the state machine at the top of the hierarchy:

#import <TBStateMachine/TBSMDebugger.h>

[[TBSMDebugger sharedInstance] debugStateMachine:stateMachine];

The statemachine will then output a log message for every event, transition, setup, teardown, enter and exit including the duration of the performed Run-to-Completion step:

[Main]: attempt to handle event 'EventA' data: 12345
[stateA] will handle event 'EventA' data: 12345
[Main] performing transition: stateA --> stateCc data: 12345
    Exit 'stateB' data: 12345
    Enter 'stateC' data: 12345
    Enter 'stateCc' data: 12345
[Main]: run-to-completion step took 1.15 milliseconds
[Main]: remaining events in queue: 1
[Main]: (

When calling -activeStateConfiguration on the debugger instance you will get the current active state configuration of the whole hierarchy:

NSLog(@"%@", [[TBSMDebugger sharedInstance] activeStateConfiguration]);

Development Setup

Clone the repo and run pod install from the Example directory first. The project contains a unit test target for development.

Useful Theory on UML State Machines


Daniel Luethi


DLStateMachine is available under the MIT license. See the LICENSE file for more info.

Latest podspec

    "name": "DLStateMachine",
    "version": "0.9.0",
    "summary": "A lightweight hierarchical state machine framework in Objective-C.",
    "description": "Supports all common features of a UML state machine like:nn- nested statesn- orthogonal regionsn- pseudo statesn- transitions with guards and actionsn- state switching using least common ancestor algorithm and run-to-completion model",
    "homepage": "",
    "license": "MIT",
    "authors": "Daniel Luethi",
    "social_media_url": "",
    "platforms": {
        "ios": "6.0",
        "watchos": "2.0",
        "tvos": "9.0",
        "osx": "10.8"
    "requires_arc": true,
    "source": {
        "git": "",
        "tag": "0.9.0"
    "default_subspecs": "Core",
    "subspecs": [
            "name": "Core",
            "source_files": "Pod/Core"
            "name": "DebugSupport",
            "source_files": "Pod/DebugSupport",
            "dependencies": {
                "DLStateMachine/Core": []

Pin It on Pinterest

Share This