Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
YBStatechart
============
YBStatechart with Payloads
==========================

Fork of YBStateChart with the ability to dispatch payloads with events, also soon to come the ability to dispatch a payload with a state change.

YBStatechart is a statechart framework. Statecharts are a formalized type of finite state machine, which resulted from [David Harel's research] [1] on software architecture design for aircraft systems in 1986. His [white paper article] [1] is well worth the (somewhat lengthy) read.

Expand Down
2 changes: 1 addition & 1 deletion YBStatechart.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "YBStatechart"
s.version = "1.0.3"
s.version = "1.0.5"
s.summary = "Framework for statecharts. A statecharts is a formalized type of finite state machine."
s.homepage = "https://github.com/ronaldmannak/YBStatechart"
s.license = 'Apache 2.0'
Expand Down
27 changes: 24 additions & 3 deletions YBStatechart.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
102591BE1521143200B11680 /* libYBStatechart.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 102591A61521143200B11680 /* libYBStatechart.a */; };
102591C71521143200B11680 /* YBStatechartTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 102591C61521143200B11680 /* YBStatechartTests.m */; };
102591D41522A3FA00B11680 /* YBStatechart.h in Headers */ = {isa = PBXBuildFile; fileRef = 102591AE1521143200B11680 /* YBStatechart.h */; settings = {ATTRIBUTES = (); }; };
813FBBD919619B6D005E0811 /* YBStatechart-Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = 813FBBD819619B6D005E0811 /* YBStatechart-Prefix.pch */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand All @@ -40,6 +41,7 @@
102591C61521143200B11680 /* YBStatechartTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = YBStatechartTests.m; sourceTree = "<group>"; };
5262D63C1651980C008A3670 /* YBStatechart.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = YBStatechart.podspec; sourceTree = SOURCE_ROOT; };
5262D6521651C3A7008A3670 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = SOURCE_ROOT; };
813FBBD819619B6D005E0811 /* YBStatechart-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "YBStatechart-Prefix.pch"; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -127,6 +129,7 @@
children = (
5262D63C1651980C008A3670 /* YBStatechart.podspec */,
5262D6521651C3A7008A3670 /* LICENSE */,
813FBBD819619B6D005E0811 /* YBStatechart-Prefix.pch */,
);
name = "Supporting Files";
sourceTree = "<group>";
Expand All @@ -139,6 +142,7 @@
buildActionMask = 2147483647;
files = (
102591D41522A3FA00B11680 /* YBStatechart.h in Headers */,
813FBBD919619B6D005E0811 /* YBStatechart-Prefix.pch in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -188,7 +192,7 @@
isa = PBXProject;
attributes = {
CLASSPREFIX = YB;
LastUpgradeCheck = 0440;
LastUpgradeCheck = 0510;
ORGANIZATIONNAME = Yobble;
};
buildConfigurationList = 102591A01521143200B11680 /* Build configuration list for PBXProject "YBStatechart" */;
Expand Down Expand Up @@ -267,8 +271,13 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
Expand All @@ -279,10 +288,14 @@
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
Expand All @@ -291,13 +304,21 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
SDKROOT = iphoneos;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Empty file.
18 changes: 17 additions & 1 deletion YBStatechart/YBStatechart.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ extern NSString *YBStateExitStateEvent;
The state object itself is passed to the block as the first argument.
@param _self : The state object to which the handler belongs.
*/
typedef void(^YBStateEventHandler)(YBState *_self);
typedef void(^YBStateEventHandler)(YBState *_self , id payload);



Expand Down Expand Up @@ -56,10 +56,17 @@ typedef void(^YBStateEventHandler)(YBState *_self);
Methods to activate a given state in the statechart, either by passing in the state object itself or by passing in the name of the state.
By default, automatic setting the historySubstate is enabled, but can be disabled using the saveToHistory argument.
*/



- (void)activateStateWithName:(NSString*)stateName;
- (void)activateStateWithName:(NSString*)stateName withPayload:(id)payload;
- (void)activateStateWithName:(NSString*)stateName saveToHistory:(BOOL)saveToHistory;
- (void)activateStateWithName:(NSString*)stateName saveToHistory:(BOOL)saveToHistory withPayload:(id)payload;
- (void)activateState:(YBState*)state;
- (void)activateState:(YBState*)state withPayload:(id)payload;
- (void)activateState:(YBState*)state saveToHistory:(BOOL)saveToHistory;
- (void)activateState:(YBState*)state saveToHistory:(BOOL)saveToHistory withPayload:(id)payload;

/**
Activates the statechart. All initial and/or history substates will be entered/activated.
Expand All @@ -86,6 +93,15 @@ typedef void(^YBStateEventHandler)(YBState *_self);
*/
- (void)dispatchEvent:(NSString*)event;

/**
Dispatches the given event to the active states in the statechart, causing the registered handlers to get called.
This method will also be called when an unknown message with no arguments and void return type is sent to a statechart, e.g.:
Sending [statechart buttonUp] will result in [statechart dispatchEvent:@"buttonUp"].
@param event - The event to dispatch.
@param payLoad - Payload to dispatch with the event for context.
*/
- (void)dispatchEvent:(NSString*)event withPayload:(id)payLoad;

@end


Expand Down
99 changes: 64 additions & 35 deletions YBStatechart/YBStatechart.m
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,18 @@ @interface YBState () {
BOOL _useHistory;
}
- (void)setStatechart:(YBStatechart*)statechart;
- (void)activate;
- (void)activateDefaultSubstatesRecursive:(BOOL)recursive saveToHistory:(BOOL)saveToHistory;
- (void)activateSubstate:(YBState*)substate saveToHistory:(BOOL)saveToHistory;
- (void)activate:(id)payload ;
- (void)activateDefaultSubstatesRecursive:(BOOL)recursive saveToHistory:(BOOL)saveToHistory withPayload:(id)payload;
- (void)activateSubstate:(YBState*)substate saveToHistory:(BOOL)saveToHistory withPayload:(id)payload ;
- (void)deactivate;
- (void)deactivateSubstatesExcept:(YBState*)exceptSubstate recursive:(BOOL)recursive;
- (void)handleEvent:(NSString*)event;
- (void)handleEvent:(NSString*)event withPayload:(id)payLoad;
- (NSMutableArray*)collectActiveSubstates;
#if DEBUG
- (BOOL)debugValidate;
#endif
@end





@implementation YBStatechart

//- (void)dealloc {
Expand Down Expand Up @@ -119,49 +115,74 @@ - (YBState*)findStateWithName:(NSString*)stateName {
return [_registeredStates objectForKey:stateName];
}

- (void)activateStateWithName:(NSString*)stateName {
[self activateStateWithName:stateName saveToHistory:YES];

- (void)activateStateWithName:(NSString*)stateName{
[self activateStateWithName:stateName withPayload:nil];
}


- (void)activateStateWithName:(NSString*)stateName saveToHistory:(BOOL)saveToHistory{
[self activateStateWithName:stateName saveToHistory:saveToHistory withPayload:nil];
}

- (void)activateState:(YBState*)state{
[self activateState:state withPayload:nil];
}


- (void)activateState:(YBState*)state saveToHistory:(BOOL)saveToHistory{
[self activateState:state saveToHistory:saveToHistory withPayload:nil];
}


- (void)activateStateWithName:(NSString*)stateName withPayload:(id)payload{
[self activateStateWithName:stateName saveToHistory:YES withPayload:payload];
}

- (void)activateStateWithName:(NSString*)stateName saveToHistory:(BOOL)saveToHistory {
- (void)activateStateWithName:(NSString*)stateName saveToHistory:(BOOL)saveToHistory withPayload:(id)payload {
YBState *state = [self findStateWithName:stateName];
NSAssert(state != nil, @"Couldn't find state with name: %@", stateName);
if (state->_active) {
return;
}
[self activateState:state saveToHistory:saveToHistory];
[self activateState:state saveToHistory:saveToHistory withPayload:payload];
}

- (void)activateState:(YBState*)state {
- (void)activateState:(YBState*)state withPayload:(id)payload{
if (state->_active) {
return;
}
[self activateState:state saveToHistory:YES];
[self activateState:state saveToHistory:YES withPayload:payload];
}

- (void)activateState:(YBState*)state saveToHistory:(BOOL)saveToHistory {
- (void)activateState:(YBState*)state saveToHistory:(BOOL)saveToHistory withPayload:(id)payload {
if (state->_active) {
return;
}
// Traverse the graph down to the root of the tree:
YBState *downState = state;
while (downState != nil) {
if (downState->_superstate) {
[downState->_superstate activateSubstate:downState saveToHistory:saveToHistory];
[downState->_superstate activateSubstate:downState saveToHistory:saveToHistory withPayload:payload
];
} else { // rootState doesn't have a superstate
[downState activate];
[downState activate:payload];
}
downState = downState->_superstate;
}
// Traverse the graph up the leaves of the tree:
[state activateDefaultSubstatesRecursive:YES saveToHistory:saveToHistory];
[state activateDefaultSubstatesRecursive:YES saveToHistory:saveToHistory withPayload:payload];
}

- (void)dispatchEvent:(NSString*)event {
- (void)dispatchEvent:(NSString*)event{
[self dispatchEvent:event withPayload:nil];
}

- (void)dispatchEvent:(NSString*)event withPayload:(id)payLoad {
NSAssert(_rootState != nil, @"No rootState set.");
NSArray* activeSubstates = [_rootState collectActiveSubstates];
[activeSubstates enumerateObjectsUsingBlock:^(YBState* state, NSUInteger idx, BOOL *stop) {
[state handleEvent: event];
[state handleEvent:event withPayload:payLoad];
}];
}

Expand Down Expand Up @@ -250,11 +271,18 @@ - (void)on:(NSString*)event doBlock:(YBStateEventHandler)handler {
}
}

#define LOG_DEBUG
- (void)onEnterState:(YBStateEventHandler)handler {
#ifdef LOG_DEBUG
NSLog(@">> %@",self.name);
#endif
[self on:YBStateEnterStateEvent doBlock:handler];
}

- (void)onExitState:(YBStateEventHandler)handler {
#ifdef LOG_DEBUG
NSLog(@"<< %@",self.name);
#endif
[self on:YBStateExitStateEvent doBlock:handler];
}

Expand Down Expand Up @@ -372,11 +400,11 @@ - (void)deactivateSubstatesExcept:(YBState*)exceptSubstate recursive:(BOOL)recur
}
}

- (void)activateDefaultSubstatesRecursive:(BOOL)recursive saveToHistory:(BOOL)saveToHistory {
- (void)activateDefaultSubstatesRecursive:(BOOL)recursive saveToHistory:(BOOL)saveToHistory withPayload:(id)payload {
if ([_substates count] == 0) {
return;
} else if ([_substates count] == 1) {
[self activateSubstate:[_substates anyObject] saveToHistory:saveToHistory];
[self activateSubstate:[_substates anyObject] saveToHistory:saveToHistory withPayload:payload];
} else {
if (_substatesAreOrthogonal == NO) {
// Figure out which substate to activate:
Expand All @@ -388,16 +416,16 @@ - (void)activateDefaultSubstatesRecursive:(BOOL)recursive saveToHistory:(BOOL)sa
defaultSubstate = [self initialSubstate];
NSAssert(defaultSubstate != nil, @"There is no initialSubstate set on `%@`. The statechart is not fully-defined!", _name);
}
[self activateSubstate:defaultSubstate saveToHistory:saveToHistory];
[self activateSubstate:defaultSubstate saveToHistory:saveToHistory withPayload:payload];
if (recursive) {
[defaultSubstate activateDefaultSubstatesRecursive:recursive saveToHistory:saveToHistory]; // recurse
[defaultSubstate activateDefaultSubstatesRecursive:recursive saveToHistory:saveToHistory withPayload:payload]; // recurse
}
} else {
// Activate all substates (they're orthogonal):
[_substates enumerateObjectsUsingBlock:^(YBState *substate, BOOL *stop) {
[self activateSubstate:substate saveToHistory:saveToHistory];
[self activateSubstate:substate saveToHistory:saveToHistory withPayload:payload];
if (recursive) {
[substate activateDefaultSubstatesRecursive:recursive saveToHistory:saveToHistory];
[substate activateDefaultSubstatesRecursive:recursive saveToHistory:saveToHistory withPayload:payload];
}
}];
}
Expand All @@ -407,32 +435,33 @@ - (void)activateDefaultSubstatesRecursive:(BOOL)recursive saveToHistory:(BOOL)sa
- (void)deactivate {
if (_active == YES) {
_active = NO;
[self handleEvent:YBStateExitStateEvent];
[self handleEvent:YBStateExitStateEvent withPayload:nil];
}
}

- (void)activate {
- (void)activate:(id)payload {
if (_active == NO) {
_active = YES;
[self handleEvent:YBStateEnterStateEvent];
[self handleEvent:YBStateEnterStateEvent withPayload:payload];
}
}

- (void)activateSubstate:(YBState*)substate saveToHistory:(BOOL)saveToHistory {

- (void)activateSubstate:(YBState*)substate saveToHistory:(BOOL)saveToHistory withPayload:(id)payload {
NSAssert([_substates containsObject:substate], @"State `%@` does not contain substate `%@`", _name, substate.name);
if (substate->_active) {
return;
} else {
if (_substatesAreOrthogonal) {
[_substates enumerateObjectsUsingBlock:^(YBState *otherSubstate, BOOL *stop) {
[otherSubstate activate];
[otherSubstate activate:payload];
}];
} else {
[self deactivateSubstatesExcept:substate recursive:YES];
if (saveToHistory) {
[self setHistorySubstate:substate];
}
[substate activate];
[substate activate:payload];
}
}
}
Expand All @@ -451,15 +480,15 @@ - (NSMutableArray *)collectActiveSubstates {
return activeSubstates;
}

- (void)handleEvent:(NSString*)event {
- (void)handleEvent:(NSString*)event withPayload:(id)payLoad {
YBStateEventHandler handler = [_eventHandlers objectForKey:event];
if (handler) {
handler(self);
handler(self,payLoad);
}
}

- (NSString*)description {
return [NSString stringWithFormat:@"<%@ %p: `%@` (%@), %i substates, statechart=%p, superstate=%p, path=%@>", [self class], self, _name, _active ? @"active" : @"inactive", [_substates count], _statechart, _superstate, [self path]];
return [NSString stringWithFormat:@"<%@ %p: `%@` (%@), %lu substates, statechart=%p, superstate=%p, path=%@>", [self class], self, _name, _active ? @"active" : @"inactive", (unsigned long)[_substates count], _statechart, _superstate, [self path]];
}

@synthesize initialSubstate = _initialSubstate;
Expand Down
Loading