View Controllers and State
          A Journey
My Main Point

Many times, view controllers have
implicit states and, if these states
are recognized and made explicit,
code may become clearer and easier
to maintain.
The record button
First Try

• The record button needs to turn red while
  the app is recording.
• The recorder needs to start recording
  from the microphone.
First Try
[recordButton setImage:@”black.png”
 forState:UIControlStateNormal];
[recordButton setImage:@"red.png"
 forState:UIControlStateSelected];

- (IBAction)recordButtonPressed:(id)sender {
!   UIButton *recordButton = (UIButton *)sender;
!   if (recordButton.selected) {
!   ! recordButton.selected = NO;
!   ! [recorder stop];
!   } else {
!   ! recordButton.selected = YES;
!   ! [recorder record];
!   }
}
What about this?
[recordButton setImage:@”black.png”
 forState:UIControlStateNormal];
[recordButton setImage:@"red.png"
 forState:UIControlStateSelected];

- (IBAction)recordButtonPressed:(id)sender {
!   UIButton *recordButton = (UIButton *)sender;
!   if (recorder.isRecording) {
!   ! recordButton.selected = NO;
!   ! [recorder stop];
!   } else {
!   ! recordButton.selected = YES;
!   ! [recorder record];
!   }
}
Problems?
• There are two independently maintained
  variables.
• Normally, they are coupled together, but
  what if something happens so that they are
  not (eg. when I handle enter background,
  interruptions, etc. might I stop the recorder
  without changing the button)?
Add a Play Button?
Add a Play Button?
- (IBAction)playButtonPressed:(id)sender {

!   UIButton *playButton = (UIButton *)sender;

!   if (playButton.selected) {
!   ! NSAssert(!recorder.isRecording, @”!!!”);
!   ! [player pause];
!   ! playButton.selected = NO;
!   } else {
!   ! [recorder stop];
!   ! recordButton.selected = NO;
!   ! [player play];
!   ! playButton.selected = YES;
!   }
}
My Main Point

Many times, view controllers have
implicit states and, if these states
are recognized and made explicit,
code may become shorter, clearer
and easier to maintain.
State?

There is a formal mathematic
definition of states, “state
machines”, “finite state automata”
that one normally studies in
computer science.
State?

I am inspired by these ideas. But
my priority is reducing the amount
of code and the complexity of code,
not in creating an implemtation of a
formal state machine.
State?
For this purpose, “states” are
discrete temporal conditions that
something (in this case, a view
controller) might be “in” which use
“transitions” to move back and
forth between each other.
State?
States are intended to be mutually
exclusive (this view controller
cannot be in a record and play state
at the same time). This is
restrictive but that is actually a
strength.
Implementing States

Implementing states involves
recognizing (naming) it and
figuring out how to transition into
it (and out of it?) correctly.
before                  after
            recording               playing
recording               recording
Using enums
typedef enum {
! BeforeRecordingState,
! RecordingState,
! AfterRecordingState,
! PlayingState
} State;

@interface RecordingController {
! State _state;
}
Dispatching
-(void)transitionToState:(State)state {

!     switch(state) {

!     !     case BeforeRecordingState:

!     !     !      [self exitBeforeRecordingState:_state];

!     !     !      break;

!     !     case RecordingState:

!     !     !      [self exitRecordingState:_state];

!     !     !      break;

...

!     !     default:

!     !     !      NSAssert(YES, @"unknown state!")

!     !     !      break;

!     }



!     _state = state;



!     switch(state) {

!     !     case BeforeRecordingState:

!     !     !      [self enterBeforeRecordingState];

!     !     !      break;

...

       }

}
Methods
-(void)enterRecordingState {
!   recordButton.selected = YES;
!   [recorder record];
}

-(void)exitRecordingState:(State)nextState {
!   NSAssert(nextState != AfterRecordingState, @”!!!"
!   recordButton.selected = NO;
!   [recorder stop]
}

-(IBAction)recordButtonPressed:(id)sender {
!   if (_state == RecordingState) {
!   !    [self transitionToState:AfterRecordingState];
!   } else {
!   !    [self transitionToState:RecordingState];! !
!   }
}
In General

I have found this kind of design is
generally better for keeping the
complexity of view controllers
under control.
My Main Point

Many times, view controllers have
implicit states and, if these states
are recognized and made explicit,
code may become clearer and easier
to maintain.
But...
I hate this.
-(void)transitionToState:(State)state {

!     switch(state) {

!     !     case BeforeRecordingState:

!     !     !      [self exitBeforeRecordingState:_state];

!     !     !      break;

!     !     case RecordingState:

!     !     !      [self exitRecordingState:_state];

!     !     !      break;

...

!     !     default:

!     !     !      NSAssert(YES, @"unknown state!")

!     !     !      break;

!     }



!     _state = state;



!     switch(state) {

!     !     case BeforeRecordingState:

!     !     !      [self enterBeforeRecordingState];

!     !     !      break;

...

       }

}
• The dispatch method must be unique for
  each view controller because each view
  controller has a unique collection of states.

• But they are identical in logic.
• This is, arguably, code duplication.
What if...

• ...the view controller ran the transitions
  automatically?
• ...states were blocks, not methods?
• ...transitions between states were
  animatable?
What if?
-(void)viewDidLoad {
    ...

!   [self addState:@"RecordingState"
!   !   enter:^{
!   !   !    recordButton.selected = YES;
!   !   !    [recorder record];
!   !   }

!   !    exit:^(NSString *previousState) {
!   !    !   recordButton.selected = NO;
!   !    !   [recorder stop];
!   !    }
!   ];

!   [self invalidateTransitionFromState:@"RecordingState" toState:@"PlayingState"];
!   ...
ChocolateBox
 Coming Soon to GitHub

ViewController/State

  • 1.
    View Controllers andState A Journey
  • 2.
    My Main Point Manytimes, view controllers have implicit states and, if these states are recognized and made explicit, code may become clearer and easier to maintain.
  • 3.
  • 4.
    First Try • Therecord button needs to turn red while the app is recording. • The recorder needs to start recording from the microphone.
  • 5.
    First Try [recordButton setImage:@”black.png” forState:UIControlStateNormal]; [recordButton setImage:@"red.png" forState:UIControlStateSelected]; - (IBAction)recordButtonPressed:(id)sender { ! UIButton *recordButton = (UIButton *)sender; ! if (recordButton.selected) { ! ! recordButton.selected = NO; ! ! [recorder stop]; ! } else { ! ! recordButton.selected = YES; ! ! [recorder record]; ! } }
  • 6.
    What about this? [recordButtonsetImage:@”black.png” forState:UIControlStateNormal]; [recordButton setImage:@"red.png" forState:UIControlStateSelected]; - (IBAction)recordButtonPressed:(id)sender { ! UIButton *recordButton = (UIButton *)sender; ! if (recorder.isRecording) { ! ! recordButton.selected = NO; ! ! [recorder stop]; ! } else { ! ! recordButton.selected = YES; ! ! [recorder record]; ! } }
  • 7.
    Problems? • There aretwo independently maintained variables. • Normally, they are coupled together, but what if something happens so that they are not (eg. when I handle enter background, interruptions, etc. might I stop the recorder without changing the button)?
  • 8.
    Add a PlayButton?
  • 9.
    Add a PlayButton? - (IBAction)playButtonPressed:(id)sender { ! UIButton *playButton = (UIButton *)sender; ! if (playButton.selected) { ! ! NSAssert(!recorder.isRecording, @”!!!”); ! ! [player pause]; ! ! playButton.selected = NO; ! } else { ! ! [recorder stop]; ! ! recordButton.selected = NO; ! ! [player play]; ! ! playButton.selected = YES; ! } }
  • 10.
    My Main Point Manytimes, view controllers have implicit states and, if these states are recognized and made explicit, code may become shorter, clearer and easier to maintain.
  • 11.
    State? There is aformal mathematic definition of states, “state machines”, “finite state automata” that one normally studies in computer science.
  • 12.
    State? I am inspiredby these ideas. But my priority is reducing the amount of code and the complexity of code, not in creating an implemtation of a formal state machine.
  • 13.
    State? For this purpose,“states” are discrete temporal conditions that something (in this case, a view controller) might be “in” which use “transitions” to move back and forth between each other.
  • 14.
    State? States are intendedto be mutually exclusive (this view controller cannot be in a record and play state at the same time). This is restrictive but that is actually a strength.
  • 15.
    Implementing States Implementing statesinvolves recognizing (naming) it and figuring out how to transition into it (and out of it?) correctly.
  • 16.
    before after recording playing recording recording
  • 17.
    Using enums typedef enum{ ! BeforeRecordingState, ! RecordingState, ! AfterRecordingState, ! PlayingState } State; @interface RecordingController { ! State _state; }
  • 18.
    Dispatching -(void)transitionToState:(State)state { ! switch(state) { ! ! case BeforeRecordingState: ! ! ! [self exitBeforeRecordingState:_state]; ! ! ! break; ! ! case RecordingState: ! ! ! [self exitRecordingState:_state]; ! ! ! break; ... ! ! default: ! ! ! NSAssert(YES, @"unknown state!") ! ! ! break; ! } ! _state = state; ! switch(state) { ! ! case BeforeRecordingState: ! ! ! [self enterBeforeRecordingState]; ! ! ! break; ... } }
  • 19.
    Methods -(void)enterRecordingState { ! recordButton.selected = YES; ! [recorder record]; } -(void)exitRecordingState:(State)nextState { ! NSAssert(nextState != AfterRecordingState, @”!!!" ! recordButton.selected = NO; ! [recorder stop] } -(IBAction)recordButtonPressed:(id)sender { ! if (_state == RecordingState) { ! ! [self transitionToState:AfterRecordingState]; ! } else { ! ! [self transitionToState:RecordingState];! ! ! } }
  • 20.
    In General I havefound this kind of design is generally better for keeping the complexity of view controllers under control.
  • 21.
    My Main Point Manytimes, view controllers have implicit states and, if these states are recognized and made explicit, code may become clearer and easier to maintain.
  • 22.
  • 23.
    I hate this. -(void)transitionToState:(State)state{ ! switch(state) { ! ! case BeforeRecordingState: ! ! ! [self exitBeforeRecordingState:_state]; ! ! ! break; ! ! case RecordingState: ! ! ! [self exitRecordingState:_state]; ! ! ! break; ... ! ! default: ! ! ! NSAssert(YES, @"unknown state!") ! ! ! break; ! } ! _state = state; ! switch(state) { ! ! case BeforeRecordingState: ! ! ! [self enterBeforeRecordingState]; ! ! ! break; ... } }
  • 24.
    • The dispatchmethod must be unique for each view controller because each view controller has a unique collection of states. • But they are identical in logic. • This is, arguably, code duplication.
  • 25.
    What if... • ...theview controller ran the transitions automatically? • ...states were blocks, not methods? • ...transitions between states were animatable?
  • 26.
    What if? -(void)viewDidLoad { ... ! [self addState:@"RecordingState" ! ! enter:^{ ! ! ! recordButton.selected = YES; ! ! ! [recorder record]; ! ! } ! ! exit:^(NSString *previousState) { ! ! ! recordButton.selected = NO; ! ! ! [recorder stop]; ! ! } ! ]; ! [self invalidateTransitionFromState:@"RecordingState" toState:@"PlayingState"]; ! ...
  • 27.