├── .swift-version ├── Package.swift ├── Tests ├── LinuxMain.swift └── StatedTests │ ├── InputArgsAndMappedStateTests.swift │ └── SimpleStatedTests.swift ├── Stated.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata ├── xcshareddata │ └── xcschemes │ │ ├── Stated-watchOS.xcscheme │ │ ├── Stated-iOS.xcscheme │ │ ├── Stated-tvOS.xcscheme │ │ └── Stated-macOS.xcscheme └── project.pbxproj ├── install_swiftenv.sh ├── Sources └── Stated │ ├── States │ ├── SimpleState.swift │ ├── StateTakingInput.swift │ ├── StateUsingMappedState.swift │ ├── State.swift │ ├── AnyState.swift │ └── StateSlot.swift │ ├── DSL │ ├── InputDSL.swift │ ├── TransitionFromStateWithMapDSL.swift │ ├── InputSlotDSL.swift │ ├── TransitionFromStateDSL.swift │ └── StateTransitionTriggerDSL.swift │ ├── Transitions │ ├── StateTransition.swift │ ├── StateTransitionTrigger.swift │ └── StateTransitionTriggerWithSideEffect.swift │ ├── Inputs │ └── InputSlot.swift │ └── Machine │ └── StateMachine.swift ├── Stated.podspec ├── Configs ├── StatedTests.plist └── Stated.plist ├── LICENSE ├── .travis.yml ├── .gitignore └── README.md /.swift-version: -------------------------------------------------------------------------------- 1 | 4.0 2 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | import PackageDescription 2 | 3 | let package = Package( 4 | name: "Stated" 5 | ) 6 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import StatedTests 3 | 4 | XCTMain([ 5 | testCase(SimpleStatedTests.allTests), 6 | testCase(InputArgsAndMappedStateTests.allTests) 7 | ]) -------------------------------------------------------------------------------- /Stated.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /install_swiftenv.sh: -------------------------------------------------------------------------------- 1 | git clone --depth 1 https://github.com/kylef/swiftenv.git ~/.swiftenv 2 | export SWIFTENV_ROOT="$HOME/.swiftenv" 3 | export PATH="$SWIFTENV_ROOT/bin:$SWIFTENV_ROOT/shims:$PATH" 4 | 5 | if [ -f ".swift-version" ] || [ -n "$SWIFT_VERSION" ]; then 6 | swiftenv install -s 7 | else 8 | swiftenv rehash 9 | fi 10 | -------------------------------------------------------------------------------- /Sources/Stated/States/SimpleState.swift: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | public protocol SimpleState: State where Arguments == NoArguments, MappedState == Void { 5 | init() 6 | } 7 | 8 | extension SimpleState { 9 | public static func create(arguments: NoArguments, state: Void) -> Self { 10 | return self.init() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sources/Stated/States/StateTakingInput.swift: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | public protocol StateTakingInput: State where MappedState == Void { 5 | static func create(arguments: Arguments) -> Self 6 | } 7 | 8 | extension StateTakingInput { 9 | public static func create(arguments: Arguments, state: Void) -> Self { 10 | return self.create(arguments: arguments) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sources/Stated/States/StateUsingMappedState.swift: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | public protocol StateUsingMappedState: State where Arguments == NoArguments { 5 | static func create(state: MappedState) -> Self 6 | } 7 | 8 | extension StateUsingMappedState { 9 | public static func create(arguments: NoArguments, state: MappedState) -> Self { 10 | return self.create(state: state) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sources/Stated/DSL/InputDSL.swift: -------------------------------------------------------------------------------- 1 | /// 2 | /// Sugar for creating an input with no arguments. 3 | /// 4 | public func input(_ friendlyName: String? = nil) -> InputSlot { 5 | return InputSlot(friendlyName) 6 | } 7 | 8 | /// 9 | /// Sugar for creating an input with arguments. 10 | /// Alternative to using `InputSlot("Input's debug name")` 11 | /// 12 | public func input(_ friendlyName: String? = nil, taking: Arguments.Type) -> InputSlot { 13 | return InputSlot(friendlyName) 14 | } 15 | -------------------------------------------------------------------------------- /Sources/Stated/States/State.swift: -------------------------------------------------------------------------------- 1 | public typealias NoArguments = Void 2 | 3 | /// 4 | /// 5 | /// 6 | public protocol State: AnyState { 7 | associatedtype Arguments = NoArguments 8 | associatedtype MappedState = Void 9 | 10 | static func create(arguments: Arguments, state: MappedState) -> Self 11 | } 12 | 13 | extension State { 14 | /// Create a slot that can only take this State in its position. 15 | /// 16 | public static var slot: StateSlot { 17 | return StateSlot() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Stated.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'Stated' 3 | s.version = '1.2.0' 4 | s.summary = 'Swift state machine with a beautiful DSL' 5 | s.homepage = 'https://github.com/jordanhamill/StateMachine' 6 | s.license = { :type => 'MIT', :file => 'LICENSE' } 7 | s.author = { 'Jordan Hamill' => 'jordan.hamill22@gmail.com' } 8 | s.ios.deployment_target = '10.0' 9 | s.osx.deployment_target = '10.9' 10 | s.source = { :git => 'https://github.com/jordanhamill/StateMachine.git', :tag => s.version.to_s } 11 | s.source_files = 'Sources/**/*' 12 | end 13 | -------------------------------------------------------------------------------- /Sources/Stated/States/AnyState.swift: -------------------------------------------------------------------------------- 1 | public protocol AnyState { } 2 | 3 | extension AnyState { 4 | /// 5 | /// Globally unique identifier for this state. 6 | /// 7 | static var stateId: String { return String(reflecting: Self.self) } 8 | 9 | /// 10 | /// Globally unique identifier for this state. 11 | /// 12 | var stateId: String { return Self.stateId } 13 | 14 | public static func ==(lhs: Self, rhs: Self) -> Bool { 15 | return lhs.stateId == rhs.stateId 16 | } 17 | } 18 | 19 | public func ==(lhs: StateSlot, rhs: AnyState) -> Bool { 20 | return lhs.stateId == rhs.stateId 21 | } 22 | 23 | public func ==(lhs: AnyState, rhs: StateSlot) -> Bool { 24 | return rhs == lhs 25 | } 26 | -------------------------------------------------------------------------------- /Configs/StatedTests.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Sources/Stated/States/StateSlot.swift: -------------------------------------------------------------------------------- 1 | public class ErasedStateSlot: Equatable, Hashable { 2 | let stateId: String 3 | 4 | fileprivate init(stateId: String) { 5 | self.stateId = stateId 6 | } 7 | 8 | public static func ==(lhs: ErasedStateSlot, rhs: ErasedStateSlot) -> Bool { 9 | return lhs.stateId == rhs.stateId 10 | } 11 | 12 | public var hashValue: Int { 13 | return stateId.hashValue 14 | } 15 | 16 | public func _to(_ to: StateSlot, map: @escaping (StateForSlot) -> StateTo.MappedState) -> StateTransition { 17 | return StateTransition(from: self, to: to, map: map) 18 | } 19 | } 20 | 21 | public class StateSlot: ErasedStateSlot { 22 | public init() { 23 | super.init(stateId: StateForSlot.stateId) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Sources/Stated/Transitions/StateTransition.swift: -------------------------------------------------------------------------------- 1 | public class StateTransition where StateTo.Arguments == Arguments { 2 | let from: ErasedStateSlot 3 | let map: (StateFrom) -> StateTo.MappedState 4 | let to: StateSlot 5 | 6 | init(from: ErasedStateSlot, to: StateSlot, map: @escaping (StateFrom) -> StateTo.MappedState) { 7 | self.from = from 8 | self.to = to 9 | self.map = map 10 | } 11 | 12 | func trigger(withInput arguments: Arguments, stateMachine: StateMachine) -> (fromState: StateFrom, toState: StateTo) { 13 | let previousState = stateMachine.currentState as! StateFrom 14 | let nextState = StateTo.create(arguments: arguments, state: map(previousState)) 15 | stateMachine.setNextState(state: nextState) 16 | 17 | return (previousState, nextState) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Configs/Stated.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.2.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2017 Jordan Hamill. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Jordan Hamill 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | matrix: 2 | include: 3 | - language: objective-c 4 | osx_image: xcode9 5 | before_script: 6 | - export LANG=en_US.UTF-8 7 | 8 | script: 9 | - xcodebuild -project Stated.xcodeproj clean 10 | - travis_retry xcodebuild -project Stated.xcodeproj -scheme Stated-iOS -destination 'platform=iOS Simulator,name=iPhone 7' -configuration Debug test 11 | 12 | - language: objective-c 13 | osx_image: xcode9 14 | before_script: 15 | - export LANG=en_US.UTF-8 16 | 17 | script: 18 | - xcodebuild -project Stated.xcodeproj clean 19 | - travis_retry xcodebuild -project Stated.xcodeproj -scheme Stated-macOS -configuration Debug test 20 | 21 | - language: generic 22 | os: linux 23 | sudo: required 24 | dist: trusty 25 | install: 26 | - eval "$(curl -sL https://gist.githubusercontent.com/kylef/5c0475ff02b7c7671d2a/raw/9f442512a46d7a2af7b850d65a7e9bd31edfb09b/swiftenv-install.sh)" 27 | - swiftenv install https://swift.org/builds/swift-4.0-release/ubuntu1404/swift-4.0-RELEASE/swift-4.0-RELEASE-ubuntu14.04.tar.gz 28 | - swift build 29 | 30 | script: 31 | - swift test -------------------------------------------------------------------------------- /Sources/Stated/Inputs/InputSlot.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct InputSlot: Equatable, Hashable, CustomDebugStringConvertible { 4 | public let friendlyName: String 5 | let uuid: String 6 | 7 | public init(_ friendlyName: String? = nil) { 8 | let uuid = UUID().uuidString 9 | self.uuid = uuid 10 | self.friendlyName = friendlyName ?? uuid 11 | } 12 | 13 | public func withArgs(_ args: Arguments) -> StateMachineInput { 14 | return { sm in 15 | guard let potentialTransitions = sm.inputToTransitionTriggers[self.uuid] else { fatalError("Undefined transition") } 16 | 17 | for erasedTransitionTrigger in potentialTransitions { 18 | let result = erasedTransitionTrigger.tryTransition(args: args, stateMachine: sm) 19 | switch result { 20 | case .noMatch: 21 | break 22 | case .triggered: 23 | return 24 | } 25 | } 26 | 27 | fatalError("Undefined transition") 28 | } 29 | } 30 | 31 | public static func ==(lhs: InputSlot, rhs: InputSlot) -> Bool { 32 | return lhs.uuid == rhs.uuid 33 | } 34 | 35 | public var hashValue: Int { 36 | return uuid.hashValue 37 | } 38 | 39 | public var debugDescription: String { 40 | return friendlyName 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/Stated/DSL/TransitionFromStateWithMapDSL.swift: -------------------------------------------------------------------------------- 1 | public struct TransitionFromStateWithMap { 2 | let transitionFromState: TransitionFromState 3 | let map: (StateFrom) -> MappedState 4 | 5 | public func to(_ to: StateSlot) -> StateTransitionTrigger 6 | where StateTo.Arguments == InputArguments, StateTo.MappedState == MappedState { 7 | let transition = transitionFromState.from._to(to, map: map) 8 | return StateTransitionTrigger(inputSlot: transitionFromState.input, transition: transition) 9 | } 10 | } 11 | 12 | /// 13 | /// Sugar for creating a mapped state transition e.g. 14 | /// An alias for `TransitionFromStateWithMap.to(_: toState)` to be used when mapping is required in order to construct the `toState`. 15 | /// ``` 16 | /// anInput.given(fromState).transition(with: { $0.prop }).to(toState) 17 | /// ``` 18 | /// Using operators you can get readable arrow structure 19 | /// ``` 20 | /// anInput | fromState => { $0.prop } => toState 21 | /// ``` 22 | /// 23 | public func => ( 24 | transitionFromStateMap: TransitionFromStateWithMap, 25 | toState: StateSlot) -> StateTransitionTrigger 26 | where StateTo.Arguments == ArgumentsForToState { 27 | return transitionFromStateMap.to(toState) 28 | } 29 | -------------------------------------------------------------------------------- /Sources/Stated/Transitions/StateTransitionTrigger.swift: -------------------------------------------------------------------------------- 1 | public class AnyStateTransitionTrigger { 2 | enum TransitionResult { 3 | case noMatch 4 | case triggered(arguments: Any, fromState: AnyState, toState: AnyState) 5 | } 6 | 7 | let inputUuid: String 8 | private let trigger: (Any, StateMachine) -> TransitionResult 9 | 10 | init(inputUuid: String, trigger: @escaping (Any, StateMachine) -> TransitionResult) { 11 | self.inputUuid = inputUuid 12 | self.trigger = trigger 13 | } 14 | 15 | func tryTransition(args: Any, stateMachine: StateMachine) -> TransitionResult { 16 | return trigger(args, stateMachine) 17 | } 18 | } 19 | 20 | public class StateTransitionTrigger: AnyStateTransitionTrigger where StateTo.Arguments == Arguments { 21 | let inputSlot: InputSlot 22 | let transition: StateTransition 23 | 24 | public init(inputSlot: InputSlot, transition: StateTransition) { 25 | self.inputSlot = inputSlot 26 | self.transition = transition 27 | super.init(inputUuid: inputSlot.uuid, trigger: { (args: Any, stateMachine: StateMachine) in 28 | guard stateMachine.currentState.stateId == transition.from.stateId else { return .noMatch } 29 | let typedArgs = args as! Arguments 30 | 31 | let (fromState, toState) = transition.trigger(withInput: typedArgs, stateMachine: stateMachine) 32 | return .triggered(arguments: args, fromState: fromState, toState: toState) 33 | }) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | .build/ 41 | 42 | # CocoaPods 43 | # 44 | # We recommend against adding the Pods directory to your .gitignore. However 45 | # you should judge for yourself, the pros and cons are mentioned at: 46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 47 | # 48 | # Pods/ 49 | 50 | # Carthage 51 | # 52 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 53 | # Carthage/Checkouts 54 | 55 | Carthage/Build 56 | 57 | # fastlane 58 | # 59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 60 | # screenshots whenever they are needed. 61 | # For more information about the recommended setup visit: 62 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 63 | 64 | fastlane/report.xml 65 | fastlane/Preview.html 66 | fastlane/screenshots 67 | fastlane/test_output 68 | -------------------------------------------------------------------------------- /Sources/Stated/Transitions/StateTransitionTriggerWithSideEffect.swift: -------------------------------------------------------------------------------- 1 | public struct SentInput: Equatable, CustomDebugStringConvertible { 2 | public let arguments: Arguments 3 | public let slot: InputSlot 4 | 5 | init(inputSlot: InputSlot, arguments: Arguments) { 6 | self.slot = inputSlot 7 | self.arguments = arguments 8 | } 9 | 10 | public static func ==(lhs: SentInput, rhs: SentInput) -> Bool { 11 | return lhs.slot.uuid == rhs.slot.uuid 12 | } 13 | 14 | public var debugDescription: String { 15 | return "Arguments: {\(arguments)} For: {\(slot)}" 16 | } 17 | } 18 | 19 | public func ==(lhs: SentInput, rhs: InputSlot) -> Bool { 20 | return lhs.slot.uuid == rhs.uuid 21 | } 22 | 23 | public class StateTransitionTriggerWithSideEffect: StateTransitionTrigger where StateTo.Arguments == Arguments { 24 | public let sideEffect: (StateMachine, StateTo, StateFrom, SentInput) -> Void 25 | 26 | public init(inputSlot: InputSlot, transition: StateTransition, 27 | sideEffect: @escaping (StateMachine, StateTo, StateFrom, SentInput) -> Void) { 28 | self.sideEffect = sideEffect 29 | super.init(inputSlot: inputSlot, transition: transition) 30 | } 31 | 32 | override func tryTransition(args: Any, stateMachine: StateMachine) -> TransitionResult { 33 | let result = super.tryTransition(args: args, stateMachine: stateMachine) 34 | switch result { 35 | case .noMatch: 36 | break 37 | case .triggered(let arguments, let fromState, let toState): 38 | let withArgs = SentInput(inputSlot: inputSlot, arguments: arguments as! Arguments) 39 | sideEffect(stateMachine, toState as! StateTo, fromState as! StateFrom, withArgs) 40 | } 41 | return result 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/Stated/DSL/InputSlotDSL.swift: -------------------------------------------------------------------------------- 1 | 2 | extension InputSlot { 3 | /// 4 | /// Define the current state the system must be in for a valid transition. 5 | /// For example a transition to `stateB` will occur when the system receives `goToB` and the current state is 6 | /// `stateA`. 7 | /// ``` 8 | /// Inputs.goToB.given(States.stateA).transition(to: States.stateB) 9 | /// ``` 10 | /// - parameter fromState: The current state constraint for the transition. 11 | /// 12 | public func given(_ fromState: StateSlot) -> TransitionFromState { 13 | return TransitionFromState(input: self, from: fromState) 14 | } 15 | 16 | /// 17 | /// Alias for `given`. 18 | /// 19 | /// Define the current state the system must be in for a valid transition. 20 | /// For example a transition to `stateB` will occur when the system receives `goToB` and the current state is 21 | /// `stateA`. 22 | /// ``` 23 | /// Inputs.goToB.from(States.stateA).transition(to: States.stateB) 24 | /// ``` 25 | /// - parameter fromState: The current state constraint for the transition. 26 | /// 27 | public func from(_ fromState: StateSlot) -> TransitionFromState { 28 | return given(fromState) 29 | } 30 | } 31 | 32 | /// 33 | /// Sugar for constructing a transition trigger given an input. 34 | /// This is an alias for `InputSlot.given` and `InputSlot.from`. 35 | /// ``` 36 | /// anInput.given(fromState).transition(to: toState) 37 | /// ``` 38 | /// Using operators you can get a table-like structure for easier reference: 39 | /// ``` 40 | /// let triggerableStateTransition = anInput | fromState => toState 41 | /// ``` 42 | /// 43 | public func |( 44 | input: InputSlot, 45 | fromState: StateSlot) 46 | -> TransitionFromState { 47 | return TransitionFromState(input: input, from: fromState) 48 | } 49 | -------------------------------------------------------------------------------- /Sources/Stated/Machine/StateMachine.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public typealias StateMachineInput = (StateMachine) -> Void 4 | 5 | public class StateMachine { 6 | // MARK: Public 7 | 8 | /// 9 | /// Triggers when the current state changes 10 | /// 11 | public var onTransition: ((AnyState) -> Void)? 12 | 13 | // MARK: Internal 14 | 15 | let mappings: [AnyStateTransitionTrigger] 16 | let inputToTransitionTriggers: [String: [AnyStateTransitionTrigger]] 17 | private(set) var currentState: AnyState 18 | 19 | // MARK: Private 20 | 21 | private let lock = NSRecursiveLock() 22 | 23 | // MARK: Lifecycle 24 | 25 | public init(initialState: InitialState, mappings: [AnyStateTransitionTrigger]) { 26 | self.currentState = initialState 27 | self.mappings = mappings 28 | 29 | var inputToTransitionTriggers: [String: [AnyStateTransitionTrigger]] = [:] 30 | for transitionTrigger in mappings { 31 | var triggers = inputToTransitionTriggers[transitionTrigger.inputUuid] ?? [] 32 | triggers.append(transitionTrigger) 33 | inputToTransitionTriggers[transitionTrigger.inputUuid] = triggers 34 | } 35 | self.inputToTransitionTriggers = inputToTransitionTriggers 36 | } 37 | 38 | // MARK: Public 39 | 40 | /// 41 | /// Send an input with arguments to trigger a state change. 42 | /// - warning: This will `fatalError` if a transition is not defined for the input + current state. 43 | /// - parameter input: Input with arguments. e.g. `anInput.withArgs(100)` 44 | /// 45 | public func send(_ input: StateMachineInput) { 46 | lock.lock(); defer { lock.unlock() } 47 | input(self) 48 | } 49 | 50 | /// 51 | /// Send an input that does not require arguments to trigger a state change. 52 | /// - warning: This will `fatalError` if a transition is not defined for the input + current state. 53 | /// - parameter input: Input without arguments. 54 | /// 55 | public func send(_ input: InputSlot) { 56 | send(input.withArgs(())) 57 | } 58 | 59 | /// 60 | /// Thread safe inspection of the current state of the system. 61 | /// - parameter inspect: A closure that has access to the current state. 62 | /// 63 | public func inspectCurrentState(inspect: (AnyState) -> Void) { 64 | lock.lock(); defer { lock.unlock() } 65 | inspect(currentState) 66 | } 67 | 68 | // MARK: Internal 69 | 70 | func setNextState(state: AnyState) { 71 | lock.lock(); defer { lock.unlock() } 72 | currentState = state 73 | onTransition?(currentState) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Stated.xcodeproj/xcshareddata/xcschemes/Stated-watchOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 46 | 47 | 53 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 71 | 72 | 73 | 74 | 76 | 77 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /Sources/Stated/DSL/TransitionFromStateDSL.swift: -------------------------------------------------------------------------------- 1 | infix operator =>: AdditionPrecedence 2 | 3 | /// 4 | /// Intermediary DSL object for constructing a transition given an input, current state and destination state. 5 | /// 6 | public struct TransitionFromState { 7 | 8 | // MARK: Public 9 | 10 | let input: InputSlot 11 | let from: StateSlot 12 | 13 | // MARK: Public 14 | 15 | public func transition(with map: @escaping (StateFrom) -> MappedState) -> TransitionFromStateWithMap { 16 | return TransitionFromStateWithMap(transitionFromState: self, map: map) 17 | } 18 | 19 | /// 20 | /// Alias for `transition(with:)` 21 | /// 22 | public func passes(_ map: @escaping (StateFrom) -> MappedState) -> TransitionFromStateWithMap { 23 | return transition(with: map) 24 | } 25 | 26 | public func transition(to: StateSlot) -> StateTransitionTrigger 27 | where StateTo.Arguments == InputArguments, StateTo.MappedState == Void { 28 | 29 | let map: (StateFrom) -> StateTo.MappedState = {_ in 30 | return () 31 | } 32 | 33 | let transition = from._to(to, map: map) 34 | return StateTransitionTrigger(inputSlot: input, transition: transition) 35 | } 36 | } 37 | 38 | 39 | /// 40 | /// Sugar for creating a state transition e.g. 41 | /// An alias for `TransitionFromState.transition(to:)` to be used when mapping is not required. 42 | /// ``` 43 | /// anInput.given(fromState).transition(to: toState) 44 | /// ``` 45 | /// Using operators you can get readable arrow structure 46 | /// ``` 47 | /// anInput | fromState => toState 48 | /// ``` 49 | /// 50 | public func => ( 51 | transitionFromState: TransitionFromState, 52 | toState: StateSlot) -> StateTransitionTrigger 53 | where StateTo.Arguments == ArgumentsForToState, StateTo.MappedState == Void { 54 | return transitionFromState.transition(to: toState) 55 | } 56 | 57 | /// 58 | /// Sugar for creating a mapped state transition e.g. 59 | /// An alias for `TransitionFromState.transition(with: { $0.prop })` to be used when mapping is required in order to construct the `toState`. 60 | /// ``` 61 | /// anInput.given(fromState).transition(with: { $0.prop }) 62 | /// ``` 63 | /// Using operators you can get readable arrow structure 64 | /// ``` 65 | /// anInput | fromState => { $0.prop } 66 | /// ``` 67 | /// 68 | public func => ( 69 | transitionFromState: TransitionFromState, 70 | map: @escaping (StateFrom) -> MappedState) -> TransitionFromStateWithMap { 71 | return transitionFromState.transition(with: map) 72 | } 73 | -------------------------------------------------------------------------------- /Stated.xcodeproj/xcshareddata/xcschemes/Stated-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /Stated.xcodeproj/xcshareddata/xcschemes/Stated-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /Stated.xcodeproj/xcshareddata/xcschemes/Stated-macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /Sources/Stated/DSL/StateTransitionTriggerDSL.swift: -------------------------------------------------------------------------------- 1 | extension StateTransitionTrigger { 2 | public func performingSideEffect(_ sideEffect: @escaping (StateMachine, StateTo, StateFrom, SentInput) -> Void) -> StateTransitionTriggerWithSideEffect { 3 | return StateTransitionTriggerWithSideEffect( 4 | inputSlot: self.inputSlot, 5 | transition: self.transition, 6 | sideEffect: sideEffect 7 | ) 8 | } 9 | 10 | public func performingSideEffect(_ sideEffect: @escaping (StateMachine, StateTo, StateFrom) -> Void) -> StateTransitionTriggerWithSideEffect { 11 | return self.performingSideEffect { stateMachine, to, from, input in 12 | sideEffect(stateMachine, to, from) 13 | } 14 | } 15 | 16 | public func performingSideEffect(_ sideEffect: @escaping (StateMachine, StateTo) -> Void) -> StateTransitionTriggerWithSideEffect { 17 | return self.performingSideEffect { stateMachine, to, _, _ in 18 | sideEffect(stateMachine, to) 19 | } 20 | } 21 | 22 | public func performingSideEffect(_ sideEffect: @escaping (StateMachine) -> Void) -> StateTransitionTriggerWithSideEffect { 23 | return self.performingSideEffect { stateMachine, _, _, _ in 24 | sideEffect(stateMachine) 25 | } 26 | } 27 | 28 | public func performingSideEffect(_ sideEffect: @escaping () -> Void) -> StateTransitionTriggerWithSideEffect { 29 | return self.performingSideEffect { _, _, _, _ in 30 | sideEffect() 31 | } 32 | } 33 | } 34 | 35 | /// 36 | /// Sugar for performing a side effect when a state transition occurs. 37 | /// This is an alias for `StateTransitionTrigger.performingSideEffect`. 38 | /// ``` 39 | /// anInput.given(fromState).transition(to: toState).performingSideEffect { stateMachine, toState, fromState, input in print("Side Effect") } 40 | /// ``` 41 | /// Using operators you can get a table-like structure for easier reference: 42 | /// ``` 43 | /// let triggerableStateTransition = anInput | fromState => toState | { stateMachine, toState, fromState, input in print("Side Effect") } 44 | /// ``` 45 | /// 46 | public func |( 47 | stateTransitionTrigger: StateTransitionTrigger, 48 | sideEffect: @escaping (StateMachine, StateTo, StateFrom, SentInput) -> Void) 49 | -> StateTransitionTriggerWithSideEffect { 50 | return stateTransitionTrigger.performingSideEffect(sideEffect) 51 | } 52 | 53 | /// 54 | /// Sugar for performing a side effect when a state transition occurs. 55 | /// This is an alias for `StateTransitionTrigger.performingSideEffect`. 56 | /// ``` 57 | /// anInput.given(fromState).transition(to: toState).performingSideEffect { stateMachine, toState, fromState in print("Side Effect") } 58 | /// ``` 59 | /// Using operators you can get a table-like structure for easier reference: 60 | /// ``` 61 | /// let triggerableStateTransition = anInput | fromState => toState | { stateMachine, toState, fromState in print("Side Effect") } 62 | /// ``` 63 | /// 64 | public func |( 65 | stateTransitionTrigger: StateTransitionTrigger, 66 | sideEffect: @escaping (StateMachine, StateTo, StateFrom) -> Void) 67 | -> StateTransitionTriggerWithSideEffect { 68 | return stateTransitionTrigger.performingSideEffect(sideEffect) 69 | } 70 | 71 | /// 72 | /// Sugar for performing a side effect when a state transition occurs. 73 | /// This is an alias for `StateTransitionTrigger.performingSideEffect`. 74 | /// ``` 75 | /// anInput.given(fromState).transition(to: toState).performingSideEffect { stateMachine, toState in print("Side Effect") } 76 | /// ``` 77 | /// Using operators you can get a table-like structure for easier reference: 78 | /// ``` 79 | /// let triggerableStateTransition = anInput | fromState => toState | { stateMachine, toState in print("Side Effect") } 80 | /// ``` 81 | /// 82 | public func |( 83 | stateTransitionTrigger: StateTransitionTrigger, 84 | sideEffect: @escaping (StateMachine, StateTo) -> Void) 85 | -> StateTransitionTriggerWithSideEffect { 86 | return stateTransitionTrigger.performingSideEffect(sideEffect) 87 | } 88 | 89 | /// 90 | /// Sugar for performing a side effect when a state transition occurs. 91 | /// This is an alias for `StateTransitionTrigger.performingSideEffect`. 92 | /// ``` 93 | /// anInput.given(fromState).transition(to: toState).performingSideEffect { stateMachine in print("Side Effect") } 94 | /// ``` 95 | /// Using operators you can get a table-like structure for easier reference: 96 | /// ``` 97 | /// let triggerableStateTransition = anInput | fromState => toState | { stateMachine in print("Side Effect") } 98 | /// ``` 99 | /// 100 | public func |( 101 | stateTransitionTrigger: StateTransitionTrigger, 102 | sideEffect: @escaping (StateMachine) -> Void) 103 | -> StateTransitionTriggerWithSideEffect { 104 | return stateTransitionTrigger.performingSideEffect(sideEffect) 105 | } 106 | 107 | /// 108 | /// Sugar for performing a side effect when a state transition occurs. 109 | /// This is an alias for `StateTransitionTrigger.performingSideEffect`. 110 | /// ``` 111 | /// anInput.given(fromState).transition(to: toState).performingSideEffect { print("Side Effect") } 112 | /// ``` 113 | /// Using operators you can get a table-like structure for easier reference: 114 | /// ``` 115 | /// let triggerableStateTransition = anInput | fromState => toState | { print("Side Effect") } 116 | /// ``` 117 | /// 118 | public func |( 119 | stateTransitionTrigger: StateTransitionTrigger, 120 | sideEffect: @escaping () -> Void) 121 | -> StateTransitionTriggerWithSideEffect { 122 | return stateTransitionTrigger.performingSideEffect(sideEffect) 123 | } 124 | -------------------------------------------------------------------------------- /Tests/StatedTests/InputArgsAndMappedStateTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Stated 3 | 4 | class InputArgsAndMappedStateTests: XCTestCase { 5 | 6 | // MARK: Dummy business domain objects 7 | 8 | struct Account { 9 | let name: String 10 | } 11 | 12 | enum LaunchedFrom { 13 | case fresh 14 | case url(URL) 15 | } 16 | 17 | enum DeepLink { 18 | case viewPost 19 | case friendRequest 20 | } 21 | 22 | // MARK: Dummy business logic state machine 23 | 24 | class AppLauncher { 25 | 26 | // MARK: States 27 | 28 | public struct UninitializedState: SimpleState { 29 | public typealias Arguments = NoArguments 30 | public typealias MappedState = Void 31 | } 32 | 33 | struct InitializedState: StateTakingInput { 34 | typealias MappedState = Void 35 | typealias Arguments = LaunchedFrom 36 | 37 | let deepLink: DeepLink? 38 | 39 | static func create(arguments: LaunchedFrom) -> InitializedState { 40 | let deepLink: DeepLink? 41 | switch arguments { 42 | case .fresh: 43 | deepLink = nil 44 | case .url: 45 | deepLink = DeepLink.viewPost 46 | } 47 | return InitializedState(deepLink: deepLink) 48 | } 49 | } 50 | 51 | struct LoggedInState: State { 52 | typealias MappedState = DeepLink? 53 | typealias Arguments = Account 54 | 55 | let deepLink: DeepLink? 56 | let account: Account 57 | 58 | static func create(arguments: Account, state: DeepLink?) -> LoggedInState { 59 | return LoggedInState(deepLink: state, account: arguments) 60 | } 61 | } 62 | 63 | public struct LoggedOutState: SimpleState { 64 | public typealias Arguments = NoArguments 65 | public typealias MappedState = Void 66 | } 67 | 68 | struct States { 69 | static let uninitialized = UninitializedState.slot 70 | static let initialized = InitializedState.slot 71 | static let loggedIn = LoggedInState.slot 72 | static let loggedOut = LoggedOutState.slot 73 | } 74 | 75 | struct Inputs { 76 | static let initialize = input("initialize", taking: LaunchedFrom.self) 77 | static let logIn = input("logIn", taking: Account.self) 78 | static let logOut = input("logOut") 79 | } 80 | 81 | // MARK: Private propteries 82 | 83 | var machine: StateMachine! 84 | 85 | // MARK: Lifecycle 86 | init(logOutOnceLoggedIn: Bool, accountName: String) { 87 | let mappings: [AnyStateTransitionTrigger] = [ 88 | Inputs.initialize 89 | .given(States.uninitialized) 90 | .transition(to: States.initialized) 91 | .performingSideEffect { (stateMachine: StateMachine) in 92 | let account = Account(name: accountName) 93 | stateMachine.send(Inputs.logIn.withArgs(account)) 94 | }, 95 | 96 | Inputs.logIn 97 | .given(States.initialized) 98 | .transition(with: { previousState in return previousState.deepLink }) 99 | .to(States.loggedIn) 100 | .performingSideEffect { (stateMachine: StateMachine) in 101 | if logOutOnceLoggedIn { 102 | stateMachine.send(Inputs.logOut) 103 | } 104 | }, 105 | 106 | Inputs.logOut 107 | .given(States.loggedIn) 108 | .transition(to: States.loggedOut) 109 | ] 110 | 111 | machine = StateMachine(initialState: UninitializedState(), mappings: mappings) 112 | } 113 | 114 | // MARK: Internal methods 115 | 116 | func initialize(from: LaunchedFrom) { 117 | machine.send(Inputs.initialize.withArgs(from)) 118 | } 119 | } 120 | 121 | func testInitalState() { 122 | let appLauncher = AppLauncher(logOutOnceLoggedIn: false, accountName: "") 123 | appLauncher.machine.inspectCurrentState { currentState in 124 | XCTAssert(currentState == AppLauncher.States.uninitialized) 125 | } 126 | } 127 | 128 | func testFinalStateWhenLoggedIn() { 129 | let appLauncher = AppLauncher(logOutOnceLoggedIn: false, accountName: "") 130 | appLauncher.initialize(from: .fresh) 131 | 132 | appLauncher.machine.inspectCurrentState { currentState in 133 | XCTAssert(currentState == AppLauncher.States.loggedIn) 134 | } 135 | } 136 | 137 | func testFinalStateWhenLoggedOut() { 138 | let appLauncher = AppLauncher(logOutOnceLoggedIn: true, accountName: "") 139 | appLauncher.initialize(from: .fresh) 140 | 141 | appLauncher.machine.inspectCurrentState { currentState in 142 | XCTAssert(currentState == AppLauncher.States.loggedOut) 143 | } 144 | } 145 | 146 | func testLocalStateReceivesInputArgumentsWhenLoggedInFromFresh() { 147 | let appLauncher = AppLauncher(logOutOnceLoggedIn: false, accountName: "Testing name") 148 | appLauncher.initialize(from: .fresh) 149 | 150 | appLauncher.machine.inspectCurrentState { currentState in 151 | XCTAssert(currentState == AppLauncher.States.loggedIn) 152 | let state = currentState as! AppLauncher.LoggedInState 153 | XCTAssertEqual(state.account.name, "Testing name") 154 | XCTAssertNil(state.deepLink) 155 | } 156 | } 157 | 158 | func testLocalStateIsMappedAndForwardedWhenLoggedInFromUrl() { 159 | let appLauncher = AppLauncher(logOutOnceLoggedIn: false, accountName: "Another name") 160 | appLauncher.initialize(from: .url(URL(fileURLWithPath: ""))) 161 | 162 | appLauncher.machine.inspectCurrentState { currentState in 163 | XCTAssert(currentState == AppLauncher.States.loggedIn) 164 | let state = currentState as! AppLauncher.LoggedInState 165 | XCTAssertEqual(state.account.name, "Another name") 166 | XCTAssertEqual(state.deepLink, .viewPost) 167 | } 168 | } 169 | 170 | static var allTests = [ 171 | ("testInitalState", testInitalState), 172 | ("testFinalStateWhenLoggedIn", testFinalStateWhenLoggedIn), 173 | ("testFinalStateWhenLoggedOut", testFinalStateWhenLoggedOut), 174 | ("testLocalStateReceivesInputArgumentsWhenLoggedInFromFresh", testLocalStateReceivesInputArgumentsWhenLoggedInFromFresh), 175 | ("testLocalStateIsMappedAndForwardedWhenLoggedInFromUrl", testLocalStateIsMappedAndForwardedWhenLoggedInFromUrl) 176 | ] 177 | } 178 | -------------------------------------------------------------------------------- /Tests/StatedTests/SimpleStatedTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Stated 3 | 4 | class SimpleStatedTests: XCTestCase { 5 | class AppLauncher { 6 | 7 | struct UninitializedState: SimpleState { } 8 | struct InitializedState: SimpleState { } 9 | struct UpgradingState: SimpleState { } 10 | struct IndexingState: SimpleState { } 11 | struct LoggedInState: SimpleState { } 12 | struct LoggedOutState: SimpleState { } 13 | 14 | struct States { 15 | static let uninitialized = UninitializedState.slot 16 | static let initialized = InitializedState.slot 17 | static let upgrading = UpgradingState.slot 18 | static let indexing = IndexingState.slot 19 | static let loggedIn = LoggedInState.slot 20 | static let loggedOut = LoggedOutState.slot 21 | } 22 | 23 | struct Inputs { 24 | static let initialize = input() 25 | static let upgrade = input() 26 | static let indexDatabase = input() 27 | static let logIn = input() 28 | static let logOut = input() 29 | } 30 | 31 | // MARK: Private propteries 32 | 33 | var machine: StateMachine! 34 | 35 | // MARK: Lifecycle 36 | 37 | init(isUpgradePending: Bool, canLogIn: Bool) { 38 | func initialize(stateMachine: StateMachine) { 39 | if isUpgradePending { 40 | stateMachine.send(Inputs.upgrade) 41 | } else { 42 | stateMachine.send(Inputs.indexDatabase) 43 | } 44 | } 45 | 46 | func upgrade(stateMachine: StateMachine) { 47 | stateMachine.send(Inputs.indexDatabase) 48 | } 49 | 50 | func indexDatabase(stateMachine: StateMachine) { 51 | if canLogIn { 52 | stateMachine.send(Inputs.logIn) 53 | } else { 54 | stateMachine.send(Inputs.logOut) 55 | } 56 | } 57 | 58 | func logIn(stateMachine: StateMachine) { 59 | stateMachine.send(Inputs.logOut) 60 | } 61 | 62 | let mappings: [AnyStateTransitionTrigger] = [ 63 | /* Input | from => to | side effect */ 64 | Inputs.initialize | States.uninitialized => States.initialized | initialize, 65 | 66 | Inputs.upgrade | States.initialized => States.upgrading | upgrade, 67 | 68 | Inputs.indexDatabase | States.upgrading => States.indexing | indexDatabase, 69 | Inputs.indexDatabase | States.initialized => States.indexing | indexDatabase, 70 | 71 | Inputs.logIn | States.indexing => States.loggedIn | logIn, 72 | 73 | Inputs.logOut | States.indexing => States.loggedOut, 74 | Inputs.logOut | States.loggedIn => States.loggedOut, 75 | ] 76 | machine = StateMachine(initialState: UninitializedState(), mappings: mappings) 77 | } 78 | 79 | // MARK: Internal methods 80 | 81 | func initialize() { 82 | machine.send(Inputs.initialize) 83 | } 84 | } 85 | 86 | func testInitalState() { 87 | let appLauncher = AppLauncher(isUpgradePending: true, canLogIn: true) 88 | appLauncher.machine.inspectCurrentState { currentState in 89 | XCTAssert(currentState == AppLauncher.States.uninitialized) 90 | } 91 | } 92 | 93 | func testFinalState() { 94 | let appLauncher = AppLauncher(isUpgradePending: true, canLogIn: true) 95 | appLauncher.initialize() 96 | 97 | appLauncher.machine.inspectCurrentState { currentState in 98 | XCTAssert(currentState == AppLauncher.States.loggedOut) 99 | } 100 | } 101 | 102 | func testVisitedStatesForUpgradingLoggedIn() { 103 | let appLauncher = AppLauncher(isUpgradePending: true, canLogIn: true) 104 | 105 | var visitedStateIds: [String] = [] 106 | let expectedStateIds: [String] = [ 107 | AppLauncher.States.initialized.stateId, 108 | AppLauncher.States.upgrading.stateId, 109 | AppLauncher.States.indexing.stateId, 110 | AppLauncher.States.loggedIn.stateId, 111 | AppLauncher.States.loggedOut.stateId, 112 | ] 113 | 114 | appLauncher.machine.onTransition = { currentState in 115 | visitedStateIds.append(currentState.stateId) 116 | } 117 | appLauncher.initialize() 118 | XCTAssertEqual(visitedStateIds, expectedStateIds) 119 | } 120 | 121 | func testVisitedStatesForUpgradingLoggedOut() { 122 | let appLauncher = AppLauncher(isUpgradePending: true, canLogIn: false) 123 | 124 | var visitedStateIds: [String] = [] 125 | let expectedStateIds: [String] = [ 126 | AppLauncher.States.initialized.stateId, 127 | AppLauncher.States.upgrading.stateId, 128 | AppLauncher.States.indexing.stateId, 129 | AppLauncher.States.loggedOut.stateId, 130 | ] 131 | 132 | appLauncher.machine.onTransition = { currentState in 133 | visitedStateIds.append(currentState.stateId) 134 | } 135 | appLauncher.initialize() 136 | XCTAssertEqual(visitedStateIds, expectedStateIds) 137 | } 138 | 139 | func testVisitedStatesForNonUpgradeLoggedIn() { 140 | let appLauncher = AppLauncher(isUpgradePending: false, canLogIn: true) 141 | 142 | var visitedStateIds: [String] = [] 143 | let expectedStateIds: [String] = [ 144 | AppLauncher.States.initialized.stateId, 145 | AppLauncher.States.indexing.stateId, 146 | AppLauncher.States.loggedIn.stateId, 147 | AppLauncher.States.loggedOut.stateId, 148 | ] 149 | 150 | appLauncher.machine.onTransition = { currentState in 151 | visitedStateIds.append(currentState.stateId) 152 | } 153 | appLauncher.initialize() 154 | XCTAssertEqual(visitedStateIds, expectedStateIds) 155 | } 156 | 157 | func testVisitedStatesForNonUpgradeLoggedOut() { 158 | let appLauncher = AppLauncher(isUpgradePending: false, canLogIn: false) 159 | 160 | var visitedStateIds: [String] = [] 161 | let expectedStateIds: [String] = [ 162 | AppLauncher.States.initialized.stateId, 163 | AppLauncher.States.indexing.stateId, 164 | AppLauncher.States.loggedOut.stateId, 165 | ] 166 | 167 | appLauncher.machine.onTransition = { currentState in 168 | visitedStateIds.append(currentState.stateId) 169 | } 170 | appLauncher.initialize() 171 | XCTAssertEqual(visitedStateIds, expectedStateIds) 172 | } 173 | 174 | static var allTests = [ 175 | ("testInitalState", testInitalState), 176 | ("testFinalState", testFinalState), 177 | ("testVisitedStatesForUpgradingLoggedIn", testVisitedStatesForUpgradingLoggedIn), 178 | ("testVisitedStatesForNonUpgradeLoggedIn", testVisitedStatesForNonUpgradeLoggedIn), 179 | ("testVisitedStatesForNonUpgradeLoggedOut", testVisitedStatesForNonUpgradeLoggedOut) 180 | ] 181 | } 182 | 183 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Stated 2 | A simple state machine implementation with a beautiful DSL. 3 | 4 | State transitions cause effects that can send a new input to the state machine, errors can be represented by new states and inputs. 5 | Each state conforms to one of `State`, `SimpleState`, `StateTakingInput` or `StateUsingMappedState`. A state object can receive arguments from a defined input. It can also be passed anything from the previous state 6 | 7 | The DSL was inspired by [RxAutomaton](https://github.com/inamiy/RxAutomaton). 8 | 9 | [![Build Status](https://travis-ci.org/jordanhamill/Stated.svg?branch=master)](https://travis-ci.org/jordanhamill/Stated) 10 | ![Swift Package Manager Compatible](https://img.shields.io/badge/SPM-compatible-brightgreen.svg) 11 | [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 12 | ![CocoaPods Compatible](https://img.shields.io/badge/pod-ios%20%7C%20macos%20%7C%20watchos%20%7C%20tvos-lightgrey.svg) 13 | 14 | ## Example State Machine 15 | 16 | App delegates can very quickly become a real handful based on the different initial screens that can appear. 17 | Below we have an app that has: 18 | - A Database Migration phase for updating local data. 19 | - A Database Indexing phase that shows a spinner. Useful if your app stores a large amount of local data. 20 | - Logged in state that shows the main `UIViewController`. 21 | - Logged out state that shows a login/signup `UIViewController`. 22 | 23 | ```swift 24 | import Stated 25 | 26 | class AppLauncher { 27 | 28 | // MARK: Create some simple states that hold no data. 29 | 30 | struct UninitializedState: SimpleState { } 31 | struct InitializedState: SimpleState { } 32 | struct UpgradingState: SimpleState { } 33 | struct IndexingState: SimpleState { } 34 | struct LoggedInState: SimpleState { } 35 | struct LoggedOutState: SimpleState { } 36 | 37 | // MARK: Define the states we're going to use by creating "slots" in which the system can place a given instance of one of our states 38 | 39 | struct States { 40 | static let uninitialized = UninitializedState.slot 41 | static let initialized = InitializedState.slot 42 | static let upgrading = UpgradingState.slot 43 | static let indexing = IndexingState.slot 44 | static let loggedIn = LoggedInState.slot 45 | static let loggedOut = LoggedOutState.slot 46 | } 47 | 48 | // MARK: Define inputs that will be used to trigger transitions between the above states 49 | 50 | struct Inputs { 51 | static let initialize = input() 52 | static let upgrade = input() 53 | static let indexDatabase = input() 54 | static let logIn = input() 55 | static let logOut = input() 56 | } 57 | 58 | // MARK: Private propteries 59 | 60 | private var machine: StateMachine! 61 | 62 | // MARK: Lifecycle 63 | 64 | init(upgradeService: Upgrade, apiService: APIService, db: PersistenceService, rootViewController: RootViewController) { 65 | 66 | // MARK: Side Effects 67 | 68 | func initialize(stateMachine: StateMachine) { 69 | if upgradeService.isUpgradePending { 70 | stateMachine.send(Inputs.upgrade) 71 | } else { 72 | stateMachine.send(Inputs.indexDatabase) 73 | } 74 | } 75 | 76 | func upgrade(stateMachine: StateMachine) { 77 | rootViewController.showUpgradeProgressController(onCompletion: { 78 | stateMachine.send(Inputs.indexDatabase) 79 | }) 80 | } 81 | 82 | func indexDatabase(stateMachine: StateMachine) { 83 | db.createSecondaryIndices(onCompletion: { 84 | if apiService.canLogIn { 85 | stateMachine.send(Inputs.logIn) 86 | } else { 87 | stateMachine.send(Inputs.logOut) 88 | } 89 | }) 90 | } 91 | 92 | func logIn(stateMachine: StateMachine) { 93 | rootViewController.showLoggedInExperience(apiService: apiService, db: db, onLogOut: { 94 | stateMachine.send(Inputs.logOut) 95 | }) 96 | } 97 | 98 | func logOut(stateMachine: StateMachine) { 99 | rootViewController.showLogInViewController(onLoggedIn: { 100 | stateMachine.send(Inputs.logIn) 101 | }) 102 | } 103 | 104 | // MARK: Define state machine using the inputs, slots and side effects from above 105 | 106 | // This is the long-form syntax and is exactly equivalent to the operator syntax below 107 | let mappings: [AnyStateTransitionTrigger] = [ 108 | Inputs.initialize 109 | .given(States.uninitialized) 110 | .transition(to: States.initialized) 111 | .performingSideEffect(initialize), 112 | 113 | Inputs.upgrade 114 | .given(States.initialized) 115 | .transition(to: States.upgrading) 116 | .performingSideEffect(upgrade), 117 | 118 | Inputs.indexDatabase 119 | .given(States.upgrading) 120 | .transition(to: States.indexing) 121 | .performingSideEffect(indexDatabase), 122 | Inputs.indexDatabase 123 | .given(States.initialized) 124 | .transition(to: States.indexing) 125 | .performingSideEffect(indexDatabase), 126 | 127 | Inputs.logIn 128 | .given(States.indexing) 129 | .transition(to: States.loggedIn) 130 | .performingSideEffect(logIn), 131 | Inputs.logIn 132 | .given(States.loggedOut) 133 | .transition(to: States.loggedIn) 134 | .performingSideEffect(logIn), 135 | 136 | Inputs.logOut 137 | .given(States.indexing) 138 | .transition(to: States.loggedOut), 139 | .performingSideEffect(logOut) 140 | Inputs.logOut 141 | .given(States.loggedIn) 142 | .transition(to: States.loggedOut) 143 | .performingSideEffect(logOut) 144 | ] 145 | 146 | // This is the shorter operator syntax and is exactly equivalent to the syntax above. 147 | // It is very easy to visualize how the system should behave in this case 148 | let mappings: [AnyStateTransitionTrigger] = [ 149 | /* Input | from => to | side effect */ 150 | Inputs.initialize | States.uninitialized => States.initialized | initialize, 151 | 152 | Inputs.upgrade | States.initialized => States.upgrading | upgrade, 153 | 154 | Inputs.indexDatabase | States.upgrading => States.indexing | indexDatabase, 155 | Inputs.indexDatabase | States.initialized => States.indexing | indexDatabase, 156 | 157 | Inputs.logIn | States.indexing => States.loggedIn | logIn, 158 | 159 | Inputs.logOut | States.indexing => States.loggedOut | logOut, 160 | Inputs.logOut | States.loggedIn => States.loggedOut | logOut, 161 | ] 162 | machine = StateMachine(initialState: UninitializedState(), mappings: mappings) 163 | } 164 | 165 | // MARK: Internal methods 166 | 167 | func initialize() { 168 | machine.send(Inputs.initialize) 169 | } 170 | } 171 | ``` 172 | 173 | ## Installation 174 | 175 | ### Cocoapods 176 | 177 | Add Stated to your Podfile: 178 | ``` 179 | source 'https://github.com/CocoaPods/Specs.git' 180 | platform :ios, '10.0' 181 | use_frameworks! 182 | 183 | target '' do 184 | pod 'Stated' 185 | end 186 | ``` 187 | 188 | Run the following command: 189 | ``` 190 | $ pod install 191 | ``` 192 | 193 | ### Carthage 194 | 195 | Add Stated to your Cartfile: 196 | ``` 197 | github "jordanhamill/Stated" 198 | ``` 199 | 200 | Run the following command: 201 | ``` 202 | $ carthage update 203 | ``` 204 | 205 | ### Swift Package Manager 206 | 207 | Add Stated as a dependency to your `package.swift` 208 | 209 | ``` 210 | dependencies: [ 211 | .Package(url: "https://github.com/jordanhamill/Stated.git", majorVersion: 1) 212 | ] 213 | ``` 214 | -------------------------------------------------------------------------------- /Stated.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 47; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 52D6D9871BEFF229002C0205 /* Stated.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D97C1BEFF229002C0205 /* Stated.framework */; }; 11 | 72483DD91F82DC8E0028400B /* StateTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DC61F82DC8E0028400B /* StateTransition.swift */; }; 12 | 72483DDA1F82DC8E0028400B /* StateTransitionTrigger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DC71F82DC8E0028400B /* StateTransitionTrigger.swift */; }; 13 | 72483DDB1F82DC8E0028400B /* StateTransitionTriggerWithSideEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DC81F82DC8E0028400B /* StateTransitionTriggerWithSideEffect.swift */; }; 14 | 72483DDC1F82DC8E0028400B /* InputSlot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DCA1F82DC8E0028400B /* InputSlot.swift */; }; 15 | 72483DDD1F82DC8E0028400B /* StateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DCB1F82DC8E0028400B /* StateMachine.swift */; }; 16 | 72483DDE1F82DC8E0028400B /* InputDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DCD1F82DC8E0028400B /* InputDSL.swift */; }; 17 | 72483DDF1F82DC8E0028400B /* InputSlotDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DCE1F82DC8E0028400B /* InputSlotDSL.swift */; }; 18 | 72483DE01F82DC8E0028400B /* StateTransitionTriggerDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DCF1F82DC8E0028400B /* StateTransitionTriggerDSL.swift */; }; 19 | 72483DE11F82DC8E0028400B /* TransitionFromStateDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD01F82DC8E0028400B /* TransitionFromStateDSL.swift */; }; 20 | 72483DE21F82DC8E0028400B /* TransitionFromStateWithMapDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD11F82DC8E0028400B /* TransitionFromStateWithMapDSL.swift */; }; 21 | 72483DE31F82DC8E0028400B /* AnyState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD31F82DC8E0028400B /* AnyState.swift */; }; 22 | 72483DE41F82DC8E0028400B /* SimpleState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD41F82DC8E0028400B /* SimpleState.swift */; }; 23 | 72483DE51F82DC8E0028400B /* State.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD51F82DC8E0028400B /* State.swift */; }; 24 | 72483DE61F82DC8E0028400B /* StateSlot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD61F82DC8E0028400B /* StateSlot.swift */; }; 25 | 72483DE71F82DC8E0028400B /* StateTakingInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD71F82DC8E0028400B /* StateTakingInput.swift */; }; 26 | 72483DE81F82DC8E0028400B /* StateUsingMappedState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD81F82DC8E0028400B /* StateUsingMappedState.swift */; }; 27 | 72483DED1F82DD570028400B /* SimpleStatedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DE91F82DCD10028400B /* SimpleStatedTests.swift */; }; 28 | 72483DEE1F82DD570028400B /* SimpleStatedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DE91F82DCD10028400B /* SimpleStatedTests.swift */; }; 29 | 72483DEF1F82DD580028400B /* SimpleStatedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DE91F82DCD10028400B /* SimpleStatedTests.swift */; }; 30 | 72483DF01F82DD730028400B /* InputArgsAndMappedStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DEA1F82DCD10028400B /* InputArgsAndMappedStateTests.swift */; }; 31 | 72483DF11F82DD740028400B /* InputArgsAndMappedStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DEA1F82DCD10028400B /* InputArgsAndMappedStateTests.swift */; }; 32 | 72483DF21F82DD740028400B /* InputArgsAndMappedStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DEA1F82DCD10028400B /* InputArgsAndMappedStateTests.swift */; }; 33 | 72483DF31F82DE9E0028400B /* InputDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DCD1F82DC8E0028400B /* InputDSL.swift */; }; 34 | 72483DF41F82DE9E0028400B /* InputSlotDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DCE1F82DC8E0028400B /* InputSlotDSL.swift */; }; 35 | 72483DF51F82DE9E0028400B /* StateTransitionTriggerDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DCF1F82DC8E0028400B /* StateTransitionTriggerDSL.swift */; }; 36 | 72483DF61F82DE9E0028400B /* TransitionFromStateDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD01F82DC8E0028400B /* TransitionFromStateDSL.swift */; }; 37 | 72483DF71F82DE9E0028400B /* TransitionFromStateWithMapDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD11F82DC8E0028400B /* TransitionFromStateWithMapDSL.swift */; }; 38 | 72483DF81F82DE9E0028400B /* InputDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DCD1F82DC8E0028400B /* InputDSL.swift */; }; 39 | 72483DF91F82DE9E0028400B /* InputSlotDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DCE1F82DC8E0028400B /* InputSlotDSL.swift */; }; 40 | 72483DFA1F82DE9E0028400B /* StateTransitionTriggerDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DCF1F82DC8E0028400B /* StateTransitionTriggerDSL.swift */; }; 41 | 72483DFB1F82DE9E0028400B /* TransitionFromStateDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD01F82DC8E0028400B /* TransitionFromStateDSL.swift */; }; 42 | 72483DFC1F82DE9E0028400B /* TransitionFromStateWithMapDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD11F82DC8E0028400B /* TransitionFromStateWithMapDSL.swift */; }; 43 | 72483DFD1F82DE9F0028400B /* InputDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DCD1F82DC8E0028400B /* InputDSL.swift */; }; 44 | 72483DFE1F82DE9F0028400B /* InputSlotDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DCE1F82DC8E0028400B /* InputSlotDSL.swift */; }; 45 | 72483DFF1F82DE9F0028400B /* StateTransitionTriggerDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DCF1F82DC8E0028400B /* StateTransitionTriggerDSL.swift */; }; 46 | 72483E001F82DE9F0028400B /* TransitionFromStateDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD01F82DC8E0028400B /* TransitionFromStateDSL.swift */; }; 47 | 72483E011F82DE9F0028400B /* TransitionFromStateWithMapDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD11F82DC8E0028400B /* TransitionFromStateWithMapDSL.swift */; }; 48 | 72483E021F82DEA20028400B /* InputSlot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DCA1F82DC8E0028400B /* InputSlot.swift */; }; 49 | 72483E031F82DEA30028400B /* InputSlot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DCA1F82DC8E0028400B /* InputSlot.swift */; }; 50 | 72483E041F82DEA30028400B /* InputSlot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DCA1F82DC8E0028400B /* InputSlot.swift */; }; 51 | 72483E051F82DEA80028400B /* AnyState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD31F82DC8E0028400B /* AnyState.swift */; }; 52 | 72483E061F82DEA80028400B /* SimpleState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD41F82DC8E0028400B /* SimpleState.swift */; }; 53 | 72483E071F82DEA80028400B /* State.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD51F82DC8E0028400B /* State.swift */; }; 54 | 72483E081F82DEA80028400B /* StateSlot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD61F82DC8E0028400B /* StateSlot.swift */; }; 55 | 72483E091F82DEA80028400B /* StateTakingInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD71F82DC8E0028400B /* StateTakingInput.swift */; }; 56 | 72483E0A1F82DEA80028400B /* StateUsingMappedState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD81F82DC8E0028400B /* StateUsingMappedState.swift */; }; 57 | 72483E0B1F82DEA90028400B /* AnyState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD31F82DC8E0028400B /* AnyState.swift */; }; 58 | 72483E0C1F82DEA90028400B /* SimpleState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD41F82DC8E0028400B /* SimpleState.swift */; }; 59 | 72483E0D1F82DEA90028400B /* State.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD51F82DC8E0028400B /* State.swift */; }; 60 | 72483E0E1F82DEA90028400B /* StateSlot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD61F82DC8E0028400B /* StateSlot.swift */; }; 61 | 72483E0F1F82DEA90028400B /* StateTakingInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD71F82DC8E0028400B /* StateTakingInput.swift */; }; 62 | 72483E101F82DEA90028400B /* StateUsingMappedState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD81F82DC8E0028400B /* StateUsingMappedState.swift */; }; 63 | 72483E111F82DEA90028400B /* AnyState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD31F82DC8E0028400B /* AnyState.swift */; }; 64 | 72483E121F82DEA90028400B /* SimpleState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD41F82DC8E0028400B /* SimpleState.swift */; }; 65 | 72483E131F82DEA90028400B /* State.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD51F82DC8E0028400B /* State.swift */; }; 66 | 72483E141F82DEA90028400B /* StateSlot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD61F82DC8E0028400B /* StateSlot.swift */; }; 67 | 72483E151F82DEA90028400B /* StateTakingInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD71F82DC8E0028400B /* StateTakingInput.swift */; }; 68 | 72483E161F82DEA90028400B /* StateUsingMappedState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DD81F82DC8E0028400B /* StateUsingMappedState.swift */; }; 69 | 72483E171F82DEAD0028400B /* StateTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DC61F82DC8E0028400B /* StateTransition.swift */; }; 70 | 72483E181F82DEAD0028400B /* StateTransitionTrigger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DC71F82DC8E0028400B /* StateTransitionTrigger.swift */; }; 71 | 72483E191F82DEAD0028400B /* StateTransitionTriggerWithSideEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DC81F82DC8E0028400B /* StateTransitionTriggerWithSideEffect.swift */; }; 72 | 72483E1A1F82DEAE0028400B /* StateTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DC61F82DC8E0028400B /* StateTransition.swift */; }; 73 | 72483E1B1F82DEAE0028400B /* StateTransitionTrigger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DC71F82DC8E0028400B /* StateTransitionTrigger.swift */; }; 74 | 72483E1C1F82DEAE0028400B /* StateTransitionTriggerWithSideEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DC81F82DC8E0028400B /* StateTransitionTriggerWithSideEffect.swift */; }; 75 | 72483E1D1F82DEAE0028400B /* StateTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DC61F82DC8E0028400B /* StateTransition.swift */; }; 76 | 72483E1E1F82DEAE0028400B /* StateTransitionTrigger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DC71F82DC8E0028400B /* StateTransitionTrigger.swift */; }; 77 | 72483E1F1F82DEAE0028400B /* StateTransitionTriggerWithSideEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DC81F82DC8E0028400B /* StateTransitionTriggerWithSideEffect.swift */; }; 78 | 72483E201F82DEB10028400B /* StateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DCB1F82DC8E0028400B /* StateMachine.swift */; }; 79 | 72483E211F82DEB20028400B /* StateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DCB1F82DC8E0028400B /* StateMachine.swift */; }; 80 | 72483E221F82DEB20028400B /* StateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72483DCB1F82DC8E0028400B /* StateMachine.swift */; }; 81 | DD7502881C68FEDE006590AF /* Stated.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6DA0F1BF000BD002C0205 /* Stated.framework */; }; 82 | DD7502921C690C7A006590AF /* Stated.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D9F01BEFFFBE002C0205 /* Stated.framework */; }; 83 | /* End PBXBuildFile section */ 84 | 85 | /* Begin PBXContainerItemProxy section */ 86 | 52D6D9881BEFF229002C0205 /* PBXContainerItemProxy */ = { 87 | isa = PBXContainerItemProxy; 88 | containerPortal = 52D6D9731BEFF229002C0205 /* Project object */; 89 | proxyType = 1; 90 | remoteGlobalIDString = 52D6D97B1BEFF229002C0205; 91 | remoteInfo = Stated; 92 | }; 93 | DD7502801C68FCFC006590AF /* PBXContainerItemProxy */ = { 94 | isa = PBXContainerItemProxy; 95 | containerPortal = 52D6D9731BEFF229002C0205 /* Project object */; 96 | proxyType = 1; 97 | remoteGlobalIDString = 52D6DA0E1BF000BD002C0205; 98 | remoteInfo = "Stated-macOS"; 99 | }; 100 | DD7502931C690C7A006590AF /* PBXContainerItemProxy */ = { 101 | isa = PBXContainerItemProxy; 102 | containerPortal = 52D6D9731BEFF229002C0205 /* Project object */; 103 | proxyType = 1; 104 | remoteGlobalIDString = 52D6D9EF1BEFFFBE002C0205; 105 | remoteInfo = "Stated-tvOS"; 106 | }; 107 | /* End PBXContainerItemProxy section */ 108 | 109 | /* Begin PBXFileReference section */ 110 | 52D6D97C1BEFF229002C0205 /* Stated.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Stated.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 111 | 52D6D9861BEFF229002C0205 /* Stated-iOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Stated-iOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 112 | 52D6D9E21BEFFF6E002C0205 /* Stated.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Stated.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 113 | 52D6D9F01BEFFFBE002C0205 /* Stated.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Stated.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 114 | 52D6DA0F1BF000BD002C0205 /* Stated.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Stated.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 115 | 72483DC61F82DC8E0028400B /* StateTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateTransition.swift; sourceTree = ""; }; 116 | 72483DC71F82DC8E0028400B /* StateTransitionTrigger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateTransitionTrigger.swift; sourceTree = ""; }; 117 | 72483DC81F82DC8E0028400B /* StateTransitionTriggerWithSideEffect.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateTransitionTriggerWithSideEffect.swift; sourceTree = ""; }; 118 | 72483DCA1F82DC8E0028400B /* InputSlot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputSlot.swift; sourceTree = ""; }; 119 | 72483DCB1F82DC8E0028400B /* StateMachine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateMachine.swift; sourceTree = ""; }; 120 | 72483DCD1F82DC8E0028400B /* InputDSL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputDSL.swift; sourceTree = ""; }; 121 | 72483DCE1F82DC8E0028400B /* InputSlotDSL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputSlotDSL.swift; sourceTree = ""; }; 122 | 72483DCF1F82DC8E0028400B /* StateTransitionTriggerDSL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateTransitionTriggerDSL.swift; sourceTree = ""; }; 123 | 72483DD01F82DC8E0028400B /* TransitionFromStateDSL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransitionFromStateDSL.swift; sourceTree = ""; }; 124 | 72483DD11F82DC8E0028400B /* TransitionFromStateWithMapDSL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransitionFromStateWithMapDSL.swift; sourceTree = ""; }; 125 | 72483DD31F82DC8E0028400B /* AnyState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnyState.swift; sourceTree = ""; }; 126 | 72483DD41F82DC8E0028400B /* SimpleState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SimpleState.swift; sourceTree = ""; }; 127 | 72483DD51F82DC8E0028400B /* State.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = State.swift; sourceTree = ""; }; 128 | 72483DD61F82DC8E0028400B /* StateSlot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateSlot.swift; sourceTree = ""; }; 129 | 72483DD71F82DC8E0028400B /* StateTakingInput.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateTakingInput.swift; sourceTree = ""; }; 130 | 72483DD81F82DC8E0028400B /* StateUsingMappedState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateUsingMappedState.swift; sourceTree = ""; }; 131 | 72483DE91F82DCD10028400B /* SimpleStatedTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SimpleStatedTests.swift; sourceTree = ""; }; 132 | 72483DEA1F82DCD10028400B /* InputArgsAndMappedStateTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputArgsAndMappedStateTests.swift; sourceTree = ""; }; 133 | AD2FAA261CD0B6D800659CF4 /* Stated.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Stated.plist; sourceTree = ""; }; 134 | AD2FAA281CD0B6E100659CF4 /* StatedTests.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = StatedTests.plist; sourceTree = ""; }; 135 | DD75027A1C68FCFC006590AF /* Stated-macOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Stated-macOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 136 | DD75028D1C690C7A006590AF /* Stated-tvOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Stated-tvOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 137 | /* End PBXFileReference section */ 138 | 139 | /* Begin PBXFrameworksBuildPhase section */ 140 | 52D6D9781BEFF229002C0205 /* Frameworks */ = { 141 | isa = PBXFrameworksBuildPhase; 142 | buildActionMask = 2147483647; 143 | files = ( 144 | ); 145 | runOnlyForDeploymentPostprocessing = 0; 146 | }; 147 | 52D6D9831BEFF229002C0205 /* Frameworks */ = { 148 | isa = PBXFrameworksBuildPhase; 149 | buildActionMask = 2147483647; 150 | files = ( 151 | 52D6D9871BEFF229002C0205 /* Stated.framework in Frameworks */, 152 | ); 153 | runOnlyForDeploymentPostprocessing = 0; 154 | }; 155 | 52D6D9DE1BEFFF6E002C0205 /* Frameworks */ = { 156 | isa = PBXFrameworksBuildPhase; 157 | buildActionMask = 2147483647; 158 | files = ( 159 | ); 160 | runOnlyForDeploymentPostprocessing = 0; 161 | }; 162 | 52D6D9EC1BEFFFBE002C0205 /* Frameworks */ = { 163 | isa = PBXFrameworksBuildPhase; 164 | buildActionMask = 2147483647; 165 | files = ( 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | 52D6DA0B1BF000BD002C0205 /* Frameworks */ = { 170 | isa = PBXFrameworksBuildPhase; 171 | buildActionMask = 2147483647; 172 | files = ( 173 | ); 174 | runOnlyForDeploymentPostprocessing = 0; 175 | }; 176 | DD7502771C68FCFC006590AF /* Frameworks */ = { 177 | isa = PBXFrameworksBuildPhase; 178 | buildActionMask = 2147483647; 179 | files = ( 180 | DD7502881C68FEDE006590AF /* Stated.framework in Frameworks */, 181 | ); 182 | runOnlyForDeploymentPostprocessing = 0; 183 | }; 184 | DD75028A1C690C7A006590AF /* Frameworks */ = { 185 | isa = PBXFrameworksBuildPhase; 186 | buildActionMask = 2147483647; 187 | files = ( 188 | DD7502921C690C7A006590AF /* Stated.framework in Frameworks */, 189 | ); 190 | runOnlyForDeploymentPostprocessing = 0; 191 | }; 192 | /* End PBXFrameworksBuildPhase section */ 193 | 194 | /* Begin PBXGroup section */ 195 | 52D6D9721BEFF229002C0205 = { 196 | isa = PBXGroup; 197 | children = ( 198 | 8933C7811EB5B7E0000D00A4 /* Sources */, 199 | 8933C7831EB5B7EB000D00A4 /* Tests */, 200 | 52D6D99C1BEFF38C002C0205 /* Configs */, 201 | 52D6D97D1BEFF229002C0205 /* Products */, 202 | ); 203 | sourceTree = ""; 204 | }; 205 | 52D6D97D1BEFF229002C0205 /* Products */ = { 206 | isa = PBXGroup; 207 | children = ( 208 | 52D6D97C1BEFF229002C0205 /* Stated.framework */, 209 | 52D6D9861BEFF229002C0205 /* Stated-iOS Tests.xctest */, 210 | 52D6D9E21BEFFF6E002C0205 /* Stated.framework */, 211 | 52D6D9F01BEFFFBE002C0205 /* Stated.framework */, 212 | 52D6DA0F1BF000BD002C0205 /* Stated.framework */, 213 | DD75027A1C68FCFC006590AF /* Stated-macOS Tests.xctest */, 214 | DD75028D1C690C7A006590AF /* Stated-tvOS Tests.xctest */, 215 | ); 216 | name = Products; 217 | sourceTree = ""; 218 | }; 219 | 52D6D99C1BEFF38C002C0205 /* Configs */ = { 220 | isa = PBXGroup; 221 | children = ( 222 | DD7502721C68FC1B006590AF /* Frameworks */, 223 | DD7502731C68FC20006590AF /* Tests */, 224 | ); 225 | path = Configs; 226 | sourceTree = ""; 227 | }; 228 | 72483DC51F82DC8E0028400B /* Transitions */ = { 229 | isa = PBXGroup; 230 | children = ( 231 | 72483DC61F82DC8E0028400B /* StateTransition.swift */, 232 | 72483DC71F82DC8E0028400B /* StateTransitionTrigger.swift */, 233 | 72483DC81F82DC8E0028400B /* StateTransitionTriggerWithSideEffect.swift */, 234 | ); 235 | path = Transitions; 236 | sourceTree = ""; 237 | }; 238 | 72483DC91F82DC8E0028400B /* Inputs */ = { 239 | isa = PBXGroup; 240 | children = ( 241 | 72483DCA1F82DC8E0028400B /* InputSlot.swift */, 242 | ); 243 | path = Inputs; 244 | sourceTree = ""; 245 | }; 246 | 72483DCC1F82DC8E0028400B /* DSL */ = { 247 | isa = PBXGroup; 248 | children = ( 249 | 72483DCD1F82DC8E0028400B /* InputDSL.swift */, 250 | 72483DCE1F82DC8E0028400B /* InputSlotDSL.swift */, 251 | 72483DCF1F82DC8E0028400B /* StateTransitionTriggerDSL.swift */, 252 | 72483DD01F82DC8E0028400B /* TransitionFromStateDSL.swift */, 253 | 72483DD11F82DC8E0028400B /* TransitionFromStateWithMapDSL.swift */, 254 | ); 255 | path = DSL; 256 | sourceTree = ""; 257 | }; 258 | 72483DD21F82DC8E0028400B /* States */ = { 259 | isa = PBXGroup; 260 | children = ( 261 | 72483DD31F82DC8E0028400B /* AnyState.swift */, 262 | 72483DD41F82DC8E0028400B /* SimpleState.swift */, 263 | 72483DD51F82DC8E0028400B /* State.swift */, 264 | 72483DD61F82DC8E0028400B /* StateSlot.swift */, 265 | 72483DD71F82DC8E0028400B /* StateTakingInput.swift */, 266 | 72483DD81F82DC8E0028400B /* StateUsingMappedState.swift */, 267 | ); 268 | path = States; 269 | sourceTree = ""; 270 | }; 271 | 72483E231F82DFDA0028400B /* Machine */ = { 272 | isa = PBXGroup; 273 | children = ( 274 | 72483DCB1F82DC8E0028400B /* StateMachine.swift */, 275 | ); 276 | path = Machine; 277 | sourceTree = ""; 278 | }; 279 | 8933C7811EB5B7E0000D00A4 /* Sources */ = { 280 | isa = PBXGroup; 281 | children = ( 282 | 72483E231F82DFDA0028400B /* Machine */, 283 | 72483DCC1F82DC8E0028400B /* DSL */, 284 | 72483DC91F82DC8E0028400B /* Inputs */, 285 | 72483DD21F82DC8E0028400B /* States */, 286 | 72483DC51F82DC8E0028400B /* Transitions */, 287 | ); 288 | name = Sources; 289 | path = Sources/Stated; 290 | sourceTree = ""; 291 | }; 292 | 8933C7831EB5B7EB000D00A4 /* Tests */ = { 293 | isa = PBXGroup; 294 | children = ( 295 | 72483DEA1F82DCD10028400B /* InputArgsAndMappedStateTests.swift */, 296 | 72483DE91F82DCD10028400B /* SimpleStatedTests.swift */, 297 | ); 298 | name = Tests; 299 | path = Tests/StatedTests; 300 | sourceTree = ""; 301 | }; 302 | DD7502721C68FC1B006590AF /* Frameworks */ = { 303 | isa = PBXGroup; 304 | children = ( 305 | AD2FAA261CD0B6D800659CF4 /* Stated.plist */, 306 | ); 307 | name = Frameworks; 308 | sourceTree = ""; 309 | }; 310 | DD7502731C68FC20006590AF /* Tests */ = { 311 | isa = PBXGroup; 312 | children = ( 313 | AD2FAA281CD0B6E100659CF4 /* StatedTests.plist */, 314 | ); 315 | name = Tests; 316 | sourceTree = ""; 317 | }; 318 | /* End PBXGroup section */ 319 | 320 | /* Begin PBXHeadersBuildPhase section */ 321 | 52D6D9791BEFF229002C0205 /* Headers */ = { 322 | isa = PBXHeadersBuildPhase; 323 | buildActionMask = 2147483647; 324 | files = ( 325 | ); 326 | runOnlyForDeploymentPostprocessing = 0; 327 | }; 328 | 52D6D9DF1BEFFF6E002C0205 /* Headers */ = { 329 | isa = PBXHeadersBuildPhase; 330 | buildActionMask = 2147483647; 331 | files = ( 332 | ); 333 | runOnlyForDeploymentPostprocessing = 0; 334 | }; 335 | 52D6D9ED1BEFFFBE002C0205 /* Headers */ = { 336 | isa = PBXHeadersBuildPhase; 337 | buildActionMask = 2147483647; 338 | files = ( 339 | ); 340 | runOnlyForDeploymentPostprocessing = 0; 341 | }; 342 | 52D6DA0C1BF000BD002C0205 /* Headers */ = { 343 | isa = PBXHeadersBuildPhase; 344 | buildActionMask = 2147483647; 345 | files = ( 346 | ); 347 | runOnlyForDeploymentPostprocessing = 0; 348 | }; 349 | /* End PBXHeadersBuildPhase section */ 350 | 351 | /* Begin PBXNativeTarget section */ 352 | 52D6D97B1BEFF229002C0205 /* Stated-iOS */ = { 353 | isa = PBXNativeTarget; 354 | buildConfigurationList = 52D6D9901BEFF229002C0205 /* Build configuration list for PBXNativeTarget "Stated-iOS" */; 355 | buildPhases = ( 356 | 52D6D9771BEFF229002C0205 /* Sources */, 357 | 52D6D9781BEFF229002C0205 /* Frameworks */, 358 | 52D6D9791BEFF229002C0205 /* Headers */, 359 | 52D6D97A1BEFF229002C0205 /* Resources */, 360 | ); 361 | buildRules = ( 362 | ); 363 | dependencies = ( 364 | ); 365 | name = "Stated-iOS"; 366 | productName = Stated; 367 | productReference = 52D6D97C1BEFF229002C0205 /* Stated.framework */; 368 | productType = "com.apple.product-type.framework"; 369 | }; 370 | 52D6D9851BEFF229002C0205 /* Stated-iOS Tests */ = { 371 | isa = PBXNativeTarget; 372 | buildConfigurationList = 52D6D9931BEFF229002C0205 /* Build configuration list for PBXNativeTarget "Stated-iOS Tests" */; 373 | buildPhases = ( 374 | 52D6D9821BEFF229002C0205 /* Sources */, 375 | 52D6D9831BEFF229002C0205 /* Frameworks */, 376 | 52D6D9841BEFF229002C0205 /* Resources */, 377 | ); 378 | buildRules = ( 379 | ); 380 | dependencies = ( 381 | 52D6D9891BEFF229002C0205 /* PBXTargetDependency */, 382 | ); 383 | name = "Stated-iOS Tests"; 384 | productName = StatedTests; 385 | productReference = 52D6D9861BEFF229002C0205 /* Stated-iOS Tests.xctest */; 386 | productType = "com.apple.product-type.bundle.unit-test"; 387 | }; 388 | 52D6D9E11BEFFF6E002C0205 /* Stated-watchOS */ = { 389 | isa = PBXNativeTarget; 390 | buildConfigurationList = 52D6D9E71BEFFF6E002C0205 /* Build configuration list for PBXNativeTarget "Stated-watchOS" */; 391 | buildPhases = ( 392 | 52D6D9DD1BEFFF6E002C0205 /* Sources */, 393 | 52D6D9DE1BEFFF6E002C0205 /* Frameworks */, 394 | 52D6D9DF1BEFFF6E002C0205 /* Headers */, 395 | 52D6D9E01BEFFF6E002C0205 /* Resources */, 396 | ); 397 | buildRules = ( 398 | ); 399 | dependencies = ( 400 | ); 401 | name = "Stated-watchOS"; 402 | productName = "Stated-watchOS"; 403 | productReference = 52D6D9E21BEFFF6E002C0205 /* Stated.framework */; 404 | productType = "com.apple.product-type.framework"; 405 | }; 406 | 52D6D9EF1BEFFFBE002C0205 /* Stated-tvOS */ = { 407 | isa = PBXNativeTarget; 408 | buildConfigurationList = 52D6DA011BEFFFBE002C0205 /* Build configuration list for PBXNativeTarget "Stated-tvOS" */; 409 | buildPhases = ( 410 | 52D6D9EB1BEFFFBE002C0205 /* Sources */, 411 | 52D6D9EC1BEFFFBE002C0205 /* Frameworks */, 412 | 52D6D9ED1BEFFFBE002C0205 /* Headers */, 413 | 52D6D9EE1BEFFFBE002C0205 /* Resources */, 414 | ); 415 | buildRules = ( 416 | ); 417 | dependencies = ( 418 | ); 419 | name = "Stated-tvOS"; 420 | productName = "Stated-tvOS"; 421 | productReference = 52D6D9F01BEFFFBE002C0205 /* Stated.framework */; 422 | productType = "com.apple.product-type.framework"; 423 | }; 424 | 52D6DA0E1BF000BD002C0205 /* Stated-macOS */ = { 425 | isa = PBXNativeTarget; 426 | buildConfigurationList = 52D6DA201BF000BD002C0205 /* Build configuration list for PBXNativeTarget "Stated-macOS" */; 427 | buildPhases = ( 428 | 52D6DA0A1BF000BD002C0205 /* Sources */, 429 | 52D6DA0B1BF000BD002C0205 /* Frameworks */, 430 | 52D6DA0C1BF000BD002C0205 /* Headers */, 431 | 52D6DA0D1BF000BD002C0205 /* Resources */, 432 | ); 433 | buildRules = ( 434 | ); 435 | dependencies = ( 436 | ); 437 | name = "Stated-macOS"; 438 | productName = "Stated-macOS"; 439 | productReference = 52D6DA0F1BF000BD002C0205 /* Stated.framework */; 440 | productType = "com.apple.product-type.framework"; 441 | }; 442 | DD7502791C68FCFC006590AF /* Stated-macOS Tests */ = { 443 | isa = PBXNativeTarget; 444 | buildConfigurationList = DD7502821C68FCFC006590AF /* Build configuration list for PBXNativeTarget "Stated-macOS Tests" */; 445 | buildPhases = ( 446 | DD7502761C68FCFC006590AF /* Sources */, 447 | DD7502771C68FCFC006590AF /* Frameworks */, 448 | DD7502781C68FCFC006590AF /* Resources */, 449 | ); 450 | buildRules = ( 451 | ); 452 | dependencies = ( 453 | DD7502811C68FCFC006590AF /* PBXTargetDependency */, 454 | ); 455 | name = "Stated-macOS Tests"; 456 | productName = "Stated-OS Tests"; 457 | productReference = DD75027A1C68FCFC006590AF /* Stated-macOS Tests.xctest */; 458 | productType = "com.apple.product-type.bundle.unit-test"; 459 | }; 460 | DD75028C1C690C7A006590AF /* Stated-tvOS Tests */ = { 461 | isa = PBXNativeTarget; 462 | buildConfigurationList = DD7502951C690C7A006590AF /* Build configuration list for PBXNativeTarget "Stated-tvOS Tests" */; 463 | buildPhases = ( 464 | DD7502891C690C7A006590AF /* Sources */, 465 | DD75028A1C690C7A006590AF /* Frameworks */, 466 | DD75028B1C690C7A006590AF /* Resources */, 467 | ); 468 | buildRules = ( 469 | ); 470 | dependencies = ( 471 | DD7502941C690C7A006590AF /* PBXTargetDependency */, 472 | ); 473 | name = "Stated-tvOS Tests"; 474 | productName = "Stated-tvOS Tests"; 475 | productReference = DD75028D1C690C7A006590AF /* Stated-tvOS Tests.xctest */; 476 | productType = "com.apple.product-type.bundle.unit-test"; 477 | }; 478 | /* End PBXNativeTarget section */ 479 | 480 | /* Begin PBXProject section */ 481 | 52D6D9731BEFF229002C0205 /* Project object */ = { 482 | isa = PBXProject; 483 | attributes = { 484 | LastSwiftUpdateCheck = 0720; 485 | LastUpgradeCheck = 0810; 486 | ORGANIZATIONNAME = "Jordan Hamill"; 487 | TargetAttributes = { 488 | 52D6D97B1BEFF229002C0205 = { 489 | CreatedOnToolsVersion = 7.1; 490 | LastSwiftMigration = 0900; 491 | }; 492 | 52D6D9851BEFF229002C0205 = { 493 | CreatedOnToolsVersion = 7.1; 494 | LastSwiftMigration = 0800; 495 | }; 496 | 52D6D9E11BEFFF6E002C0205 = { 497 | CreatedOnToolsVersion = 7.1; 498 | LastSwiftMigration = 0800; 499 | }; 500 | 52D6D9EF1BEFFFBE002C0205 = { 501 | CreatedOnToolsVersion = 7.1; 502 | LastSwiftMigration = 0800; 503 | }; 504 | 52D6DA0E1BF000BD002C0205 = { 505 | CreatedOnToolsVersion = 7.1; 506 | LastSwiftMigration = 0800; 507 | }; 508 | DD7502791C68FCFC006590AF = { 509 | CreatedOnToolsVersion = 7.2.1; 510 | LastSwiftMigration = 0800; 511 | }; 512 | DD75028C1C690C7A006590AF = { 513 | CreatedOnToolsVersion = 7.2.1; 514 | LastSwiftMigration = 0800; 515 | }; 516 | }; 517 | }; 518 | buildConfigurationList = 52D6D9761BEFF229002C0205 /* Build configuration list for PBXProject "Stated" */; 519 | compatibilityVersion = "Xcode 6.3"; 520 | developmentRegion = English; 521 | hasScannedForEncodings = 0; 522 | knownRegions = ( 523 | en, 524 | ); 525 | mainGroup = 52D6D9721BEFF229002C0205; 526 | productRefGroup = 52D6D97D1BEFF229002C0205 /* Products */; 527 | projectDirPath = ""; 528 | projectRoot = ""; 529 | targets = ( 530 | 52D6D97B1BEFF229002C0205 /* Stated-iOS */, 531 | 52D6DA0E1BF000BD002C0205 /* Stated-macOS */, 532 | 52D6D9E11BEFFF6E002C0205 /* Stated-watchOS */, 533 | 52D6D9EF1BEFFFBE002C0205 /* Stated-tvOS */, 534 | 52D6D9851BEFF229002C0205 /* Stated-iOS Tests */, 535 | DD7502791C68FCFC006590AF /* Stated-macOS Tests */, 536 | DD75028C1C690C7A006590AF /* Stated-tvOS Tests */, 537 | ); 538 | }; 539 | /* End PBXProject section */ 540 | 541 | /* Begin PBXResourcesBuildPhase section */ 542 | 52D6D97A1BEFF229002C0205 /* Resources */ = { 543 | isa = PBXResourcesBuildPhase; 544 | buildActionMask = 2147483647; 545 | files = ( 546 | ); 547 | runOnlyForDeploymentPostprocessing = 0; 548 | }; 549 | 52D6D9841BEFF229002C0205 /* Resources */ = { 550 | isa = PBXResourcesBuildPhase; 551 | buildActionMask = 2147483647; 552 | files = ( 553 | ); 554 | runOnlyForDeploymentPostprocessing = 0; 555 | }; 556 | 52D6D9E01BEFFF6E002C0205 /* Resources */ = { 557 | isa = PBXResourcesBuildPhase; 558 | buildActionMask = 2147483647; 559 | files = ( 560 | ); 561 | runOnlyForDeploymentPostprocessing = 0; 562 | }; 563 | 52D6D9EE1BEFFFBE002C0205 /* Resources */ = { 564 | isa = PBXResourcesBuildPhase; 565 | buildActionMask = 2147483647; 566 | files = ( 567 | ); 568 | runOnlyForDeploymentPostprocessing = 0; 569 | }; 570 | 52D6DA0D1BF000BD002C0205 /* Resources */ = { 571 | isa = PBXResourcesBuildPhase; 572 | buildActionMask = 2147483647; 573 | files = ( 574 | ); 575 | runOnlyForDeploymentPostprocessing = 0; 576 | }; 577 | DD7502781C68FCFC006590AF /* Resources */ = { 578 | isa = PBXResourcesBuildPhase; 579 | buildActionMask = 2147483647; 580 | files = ( 581 | ); 582 | runOnlyForDeploymentPostprocessing = 0; 583 | }; 584 | DD75028B1C690C7A006590AF /* Resources */ = { 585 | isa = PBXResourcesBuildPhase; 586 | buildActionMask = 2147483647; 587 | files = ( 588 | ); 589 | runOnlyForDeploymentPostprocessing = 0; 590 | }; 591 | /* End PBXResourcesBuildPhase section */ 592 | 593 | /* Begin PBXSourcesBuildPhase section */ 594 | 52D6D9771BEFF229002C0205 /* Sources */ = { 595 | isa = PBXSourcesBuildPhase; 596 | buildActionMask = 2147483647; 597 | files = ( 598 | 72483DDE1F82DC8E0028400B /* InputDSL.swift in Sources */, 599 | 72483DDC1F82DC8E0028400B /* InputSlot.swift in Sources */, 600 | 72483DDB1F82DC8E0028400B /* StateTransitionTriggerWithSideEffect.swift in Sources */, 601 | 72483DE61F82DC8E0028400B /* StateSlot.swift in Sources */, 602 | 72483DE41F82DC8E0028400B /* SimpleState.swift in Sources */, 603 | 72483DE81F82DC8E0028400B /* StateUsingMappedState.swift in Sources */, 604 | 72483DD91F82DC8E0028400B /* StateTransition.swift in Sources */, 605 | 72483DDF1F82DC8E0028400B /* InputSlotDSL.swift in Sources */, 606 | 72483DE01F82DC8E0028400B /* StateTransitionTriggerDSL.swift in Sources */, 607 | 72483DDD1F82DC8E0028400B /* StateMachine.swift in Sources */, 608 | 72483DE21F82DC8E0028400B /* TransitionFromStateWithMapDSL.swift in Sources */, 609 | 72483DE51F82DC8E0028400B /* State.swift in Sources */, 610 | 72483DE11F82DC8E0028400B /* TransitionFromStateDSL.swift in Sources */, 611 | 72483DE31F82DC8E0028400B /* AnyState.swift in Sources */, 612 | 72483DE71F82DC8E0028400B /* StateTakingInput.swift in Sources */, 613 | 72483DDA1F82DC8E0028400B /* StateTransitionTrigger.swift in Sources */, 614 | ); 615 | runOnlyForDeploymentPostprocessing = 0; 616 | }; 617 | 52D6D9821BEFF229002C0205 /* Sources */ = { 618 | isa = PBXSourcesBuildPhase; 619 | buildActionMask = 2147483647; 620 | files = ( 621 | 72483DF01F82DD730028400B /* InputArgsAndMappedStateTests.swift in Sources */, 622 | 72483DED1F82DD570028400B /* SimpleStatedTests.swift in Sources */, 623 | ); 624 | runOnlyForDeploymentPostprocessing = 0; 625 | }; 626 | 52D6D9DD1BEFFF6E002C0205 /* Sources */ = { 627 | isa = PBXSourcesBuildPhase; 628 | buildActionMask = 2147483647; 629 | files = ( 630 | 72483E1C1F82DEAE0028400B /* StateTransitionTriggerWithSideEffect.swift in Sources */, 631 | 72483E031F82DEA30028400B /* InputSlot.swift in Sources */, 632 | 72483E0F1F82DEA90028400B /* StateTakingInput.swift in Sources */, 633 | 72483E211F82DEB20028400B /* StateMachine.swift in Sources */, 634 | 72483DFA1F82DE9E0028400B /* StateTransitionTriggerDSL.swift in Sources */, 635 | 72483E1B1F82DEAE0028400B /* StateTransitionTrigger.swift in Sources */, 636 | 72483DF91F82DE9E0028400B /* InputSlotDSL.swift in Sources */, 637 | 72483DFC1F82DE9E0028400B /* TransitionFromStateWithMapDSL.swift in Sources */, 638 | 72483E101F82DEA90028400B /* StateUsingMappedState.swift in Sources */, 639 | 72483DFB1F82DE9E0028400B /* TransitionFromStateDSL.swift in Sources */, 640 | 72483E0E1F82DEA90028400B /* StateSlot.swift in Sources */, 641 | 72483E0D1F82DEA90028400B /* State.swift in Sources */, 642 | 72483DF81F82DE9E0028400B /* InputDSL.swift in Sources */, 643 | 72483E0B1F82DEA90028400B /* AnyState.swift in Sources */, 644 | 72483E0C1F82DEA90028400B /* SimpleState.swift in Sources */, 645 | 72483E1A1F82DEAE0028400B /* StateTransition.swift in Sources */, 646 | ); 647 | runOnlyForDeploymentPostprocessing = 0; 648 | }; 649 | 52D6D9EB1BEFFFBE002C0205 /* Sources */ = { 650 | isa = PBXSourcesBuildPhase; 651 | buildActionMask = 2147483647; 652 | files = ( 653 | 72483E1F1F82DEAE0028400B /* StateTransitionTriggerWithSideEffect.swift in Sources */, 654 | 72483E041F82DEA30028400B /* InputSlot.swift in Sources */, 655 | 72483E151F82DEA90028400B /* StateTakingInput.swift in Sources */, 656 | 72483E221F82DEB20028400B /* StateMachine.swift in Sources */, 657 | 72483DFF1F82DE9F0028400B /* StateTransitionTriggerDSL.swift in Sources */, 658 | 72483E1E1F82DEAE0028400B /* StateTransitionTrigger.swift in Sources */, 659 | 72483DFE1F82DE9F0028400B /* InputSlotDSL.swift in Sources */, 660 | 72483E011F82DE9F0028400B /* TransitionFromStateWithMapDSL.swift in Sources */, 661 | 72483E161F82DEA90028400B /* StateUsingMappedState.swift in Sources */, 662 | 72483E001F82DE9F0028400B /* TransitionFromStateDSL.swift in Sources */, 663 | 72483E141F82DEA90028400B /* StateSlot.swift in Sources */, 664 | 72483E131F82DEA90028400B /* State.swift in Sources */, 665 | 72483DFD1F82DE9F0028400B /* InputDSL.swift in Sources */, 666 | 72483E111F82DEA90028400B /* AnyState.swift in Sources */, 667 | 72483E121F82DEA90028400B /* SimpleState.swift in Sources */, 668 | 72483E1D1F82DEAE0028400B /* StateTransition.swift in Sources */, 669 | ); 670 | runOnlyForDeploymentPostprocessing = 0; 671 | }; 672 | 52D6DA0A1BF000BD002C0205 /* Sources */ = { 673 | isa = PBXSourcesBuildPhase; 674 | buildActionMask = 2147483647; 675 | files = ( 676 | 72483E191F82DEAD0028400B /* StateTransitionTriggerWithSideEffect.swift in Sources */, 677 | 72483E021F82DEA20028400B /* InputSlot.swift in Sources */, 678 | 72483E091F82DEA80028400B /* StateTakingInput.swift in Sources */, 679 | 72483E201F82DEB10028400B /* StateMachine.swift in Sources */, 680 | 72483DF51F82DE9E0028400B /* StateTransitionTriggerDSL.swift in Sources */, 681 | 72483E181F82DEAD0028400B /* StateTransitionTrigger.swift in Sources */, 682 | 72483DF41F82DE9E0028400B /* InputSlotDSL.swift in Sources */, 683 | 72483DF71F82DE9E0028400B /* TransitionFromStateWithMapDSL.swift in Sources */, 684 | 72483E0A1F82DEA80028400B /* StateUsingMappedState.swift in Sources */, 685 | 72483DF61F82DE9E0028400B /* TransitionFromStateDSL.swift in Sources */, 686 | 72483E081F82DEA80028400B /* StateSlot.swift in Sources */, 687 | 72483E071F82DEA80028400B /* State.swift in Sources */, 688 | 72483DF31F82DE9E0028400B /* InputDSL.swift in Sources */, 689 | 72483E051F82DEA80028400B /* AnyState.swift in Sources */, 690 | 72483E061F82DEA80028400B /* SimpleState.swift in Sources */, 691 | 72483E171F82DEAD0028400B /* StateTransition.swift in Sources */, 692 | ); 693 | runOnlyForDeploymentPostprocessing = 0; 694 | }; 695 | DD7502761C68FCFC006590AF /* Sources */ = { 696 | isa = PBXSourcesBuildPhase; 697 | buildActionMask = 2147483647; 698 | files = ( 699 | 72483DF11F82DD740028400B /* InputArgsAndMappedStateTests.swift in Sources */, 700 | 72483DEE1F82DD570028400B /* SimpleStatedTests.swift in Sources */, 701 | ); 702 | runOnlyForDeploymentPostprocessing = 0; 703 | }; 704 | DD7502891C690C7A006590AF /* Sources */ = { 705 | isa = PBXSourcesBuildPhase; 706 | buildActionMask = 2147483647; 707 | files = ( 708 | 72483DF21F82DD740028400B /* InputArgsAndMappedStateTests.swift in Sources */, 709 | 72483DEF1F82DD580028400B /* SimpleStatedTests.swift in Sources */, 710 | ); 711 | runOnlyForDeploymentPostprocessing = 0; 712 | }; 713 | /* End PBXSourcesBuildPhase section */ 714 | 715 | /* Begin PBXTargetDependency section */ 716 | 52D6D9891BEFF229002C0205 /* PBXTargetDependency */ = { 717 | isa = PBXTargetDependency; 718 | target = 52D6D97B1BEFF229002C0205 /* Stated-iOS */; 719 | targetProxy = 52D6D9881BEFF229002C0205 /* PBXContainerItemProxy */; 720 | }; 721 | DD7502811C68FCFC006590AF /* PBXTargetDependency */ = { 722 | isa = PBXTargetDependency; 723 | target = 52D6DA0E1BF000BD002C0205 /* Stated-macOS */; 724 | targetProxy = DD7502801C68FCFC006590AF /* PBXContainerItemProxy */; 725 | }; 726 | DD7502941C690C7A006590AF /* PBXTargetDependency */ = { 727 | isa = PBXTargetDependency; 728 | target = 52D6D9EF1BEFFFBE002C0205 /* Stated-tvOS */; 729 | targetProxy = DD7502931C690C7A006590AF /* PBXContainerItemProxy */; 730 | }; 731 | /* End PBXTargetDependency section */ 732 | 733 | /* Begin XCBuildConfiguration section */ 734 | 52D6D98E1BEFF229002C0205 /* Debug */ = { 735 | isa = XCBuildConfiguration; 736 | buildSettings = { 737 | ALWAYS_SEARCH_USER_PATHS = NO; 738 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 739 | CLANG_CXX_LIBRARY = "libc++"; 740 | CLANG_ENABLE_MODULES = YES; 741 | CLANG_ENABLE_OBJC_ARC = YES; 742 | CLANG_WARN_BOOL_CONVERSION = YES; 743 | CLANG_WARN_CONSTANT_CONVERSION = YES; 744 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 745 | CLANG_WARN_EMPTY_BODY = YES; 746 | CLANG_WARN_ENUM_CONVERSION = YES; 747 | CLANG_WARN_INFINITE_RECURSION = YES; 748 | CLANG_WARN_INT_CONVERSION = YES; 749 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 750 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 751 | CLANG_WARN_UNREACHABLE_CODE = YES; 752 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 753 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 754 | COPY_PHASE_STRIP = NO; 755 | CURRENT_PROJECT_VERSION = 1; 756 | DEBUG_INFORMATION_FORMAT = dwarf; 757 | ENABLE_STRICT_OBJC_MSGSEND = YES; 758 | ENABLE_TESTABILITY = YES; 759 | GCC_C_LANGUAGE_STANDARD = gnu99; 760 | GCC_DYNAMIC_NO_PIC = NO; 761 | GCC_NO_COMMON_BLOCKS = YES; 762 | GCC_OPTIMIZATION_LEVEL = 0; 763 | GCC_PREPROCESSOR_DEFINITIONS = ( 764 | "DEBUG=1", 765 | "$(inherited)", 766 | ); 767 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 768 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 769 | GCC_WARN_UNDECLARED_SELECTOR = YES; 770 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 771 | GCC_WARN_UNUSED_FUNCTION = YES; 772 | GCC_WARN_UNUSED_VARIABLE = YES; 773 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 774 | MTL_ENABLE_DEBUG_INFO = YES; 775 | ONLY_ACTIVE_ARCH = YES; 776 | SDKROOT = iphoneos; 777 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 778 | SWIFT_VERSION = 3.0; 779 | TARGETED_DEVICE_FAMILY = "1,2"; 780 | VERSIONING_SYSTEM = "apple-generic"; 781 | VERSION_INFO_PREFIX = ""; 782 | }; 783 | name = Debug; 784 | }; 785 | 52D6D98F1BEFF229002C0205 /* Release */ = { 786 | isa = XCBuildConfiguration; 787 | buildSettings = { 788 | ALWAYS_SEARCH_USER_PATHS = NO; 789 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 790 | CLANG_CXX_LIBRARY = "libc++"; 791 | CLANG_ENABLE_MODULES = YES; 792 | CLANG_ENABLE_OBJC_ARC = YES; 793 | CLANG_WARN_BOOL_CONVERSION = YES; 794 | CLANG_WARN_CONSTANT_CONVERSION = YES; 795 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 796 | CLANG_WARN_EMPTY_BODY = YES; 797 | CLANG_WARN_ENUM_CONVERSION = YES; 798 | CLANG_WARN_INFINITE_RECURSION = YES; 799 | CLANG_WARN_INT_CONVERSION = YES; 800 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 801 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 802 | CLANG_WARN_UNREACHABLE_CODE = YES; 803 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 804 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 805 | COPY_PHASE_STRIP = NO; 806 | CURRENT_PROJECT_VERSION = 1; 807 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 808 | ENABLE_NS_ASSERTIONS = NO; 809 | ENABLE_STRICT_OBJC_MSGSEND = YES; 810 | GCC_C_LANGUAGE_STANDARD = gnu99; 811 | GCC_NO_COMMON_BLOCKS = YES; 812 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 813 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 814 | GCC_WARN_UNDECLARED_SELECTOR = YES; 815 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 816 | GCC_WARN_UNUSED_FUNCTION = YES; 817 | GCC_WARN_UNUSED_VARIABLE = YES; 818 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 819 | MTL_ENABLE_DEBUG_INFO = NO; 820 | SDKROOT = iphoneos; 821 | SWIFT_VERSION = 3.0; 822 | TARGETED_DEVICE_FAMILY = "1,2"; 823 | VALIDATE_PRODUCT = YES; 824 | VERSIONING_SYSTEM = "apple-generic"; 825 | VERSION_INFO_PREFIX = ""; 826 | }; 827 | name = Release; 828 | }; 829 | 52D6D9911BEFF229002C0205 /* Debug */ = { 830 | isa = XCBuildConfiguration; 831 | buildSettings = { 832 | APPLICATION_EXTENSION_API_ONLY = YES; 833 | CLANG_ENABLE_MODULES = YES; 834 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 835 | DEFINES_MODULE = YES; 836 | DYLIB_COMPATIBILITY_VERSION = 1; 837 | DYLIB_CURRENT_VERSION = 1; 838 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 839 | INFOPLIST_FILE = Configs/Stated.plist; 840 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 841 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 842 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 843 | ONLY_ACTIVE_ARCH = NO; 844 | PRODUCT_BUNDLE_IDENTIFIER = "com.Stated.Stated-iOS"; 845 | PRODUCT_NAME = Stated; 846 | SKIP_INSTALL = YES; 847 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 848 | SWIFT_VERSION = 3.0; 849 | }; 850 | name = Debug; 851 | }; 852 | 52D6D9921BEFF229002C0205 /* Release */ = { 853 | isa = XCBuildConfiguration; 854 | buildSettings = { 855 | APPLICATION_EXTENSION_API_ONLY = YES; 856 | CLANG_ENABLE_MODULES = YES; 857 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 858 | DEFINES_MODULE = YES; 859 | DYLIB_COMPATIBILITY_VERSION = 1; 860 | DYLIB_CURRENT_VERSION = 1; 861 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 862 | INFOPLIST_FILE = Configs/Stated.plist; 863 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 864 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 865 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 866 | PRODUCT_BUNDLE_IDENTIFIER = "com.Stated.Stated-iOS"; 867 | PRODUCT_NAME = Stated; 868 | SKIP_INSTALL = YES; 869 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 870 | SWIFT_VERSION = 3.0; 871 | }; 872 | name = Release; 873 | }; 874 | 52D6D9941BEFF229002C0205 /* Debug */ = { 875 | isa = XCBuildConfiguration; 876 | buildSettings = { 877 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 878 | CLANG_ENABLE_MODULES = YES; 879 | INFOPLIST_FILE = Configs/StatedTests.plist; 880 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 881 | PRODUCT_BUNDLE_IDENTIFIER = "com.Stated.Stated-iOS-Tests"; 882 | PRODUCT_NAME = "$(TARGET_NAME)"; 883 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 884 | SWIFT_VERSION = 3.0; 885 | }; 886 | name = Debug; 887 | }; 888 | 52D6D9951BEFF229002C0205 /* Release */ = { 889 | isa = XCBuildConfiguration; 890 | buildSettings = { 891 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 892 | CLANG_ENABLE_MODULES = YES; 893 | INFOPLIST_FILE = Configs/StatedTests.plist; 894 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 895 | PRODUCT_BUNDLE_IDENTIFIER = "com.Stated.Stated-iOS-Tests"; 896 | PRODUCT_NAME = "$(TARGET_NAME)"; 897 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 898 | SWIFT_VERSION = 3.0; 899 | }; 900 | name = Release; 901 | }; 902 | 52D6D9E81BEFFF6E002C0205 /* Debug */ = { 903 | isa = XCBuildConfiguration; 904 | buildSettings = { 905 | APPLICATION_EXTENSION_API_ONLY = YES; 906 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 907 | DEFINES_MODULE = YES; 908 | DYLIB_COMPATIBILITY_VERSION = 1; 909 | DYLIB_CURRENT_VERSION = 1; 910 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 911 | INFOPLIST_FILE = Configs/Stated.plist; 912 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 913 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 914 | PRODUCT_BUNDLE_IDENTIFIER = "com.Stated.Stated-watchOS"; 915 | PRODUCT_NAME = Stated; 916 | SDKROOT = watchos; 917 | SKIP_INSTALL = YES; 918 | SWIFT_VERSION = 3.0; 919 | TARGETED_DEVICE_FAMILY = 4; 920 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 921 | }; 922 | name = Debug; 923 | }; 924 | 52D6D9E91BEFFF6E002C0205 /* Release */ = { 925 | isa = XCBuildConfiguration; 926 | buildSettings = { 927 | APPLICATION_EXTENSION_API_ONLY = YES; 928 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 929 | DEFINES_MODULE = YES; 930 | DYLIB_COMPATIBILITY_VERSION = 1; 931 | DYLIB_CURRENT_VERSION = 1; 932 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 933 | INFOPLIST_FILE = Configs/Stated.plist; 934 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 935 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 936 | PRODUCT_BUNDLE_IDENTIFIER = "com.Stated.Stated-watchOS"; 937 | PRODUCT_NAME = Stated; 938 | SDKROOT = watchos; 939 | SKIP_INSTALL = YES; 940 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 941 | SWIFT_VERSION = 3.0; 942 | TARGETED_DEVICE_FAMILY = 4; 943 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 944 | }; 945 | name = Release; 946 | }; 947 | 52D6DA021BEFFFBE002C0205 /* Debug */ = { 948 | isa = XCBuildConfiguration; 949 | buildSettings = { 950 | APPLICATION_EXTENSION_API_ONLY = YES; 951 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 952 | DEFINES_MODULE = YES; 953 | DYLIB_COMPATIBILITY_VERSION = 1; 954 | DYLIB_CURRENT_VERSION = 1; 955 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 956 | INFOPLIST_FILE = Configs/Stated.plist; 957 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 958 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 959 | PRODUCT_BUNDLE_IDENTIFIER = "com.Stated.Stated-tvOS"; 960 | PRODUCT_NAME = Stated; 961 | SDKROOT = appletvos; 962 | SKIP_INSTALL = YES; 963 | SWIFT_VERSION = 3.0; 964 | TARGETED_DEVICE_FAMILY = 3; 965 | TVOS_DEPLOYMENT_TARGET = 9.0; 966 | }; 967 | name = Debug; 968 | }; 969 | 52D6DA031BEFFFBE002C0205 /* Release */ = { 970 | isa = XCBuildConfiguration; 971 | buildSettings = { 972 | APPLICATION_EXTENSION_API_ONLY = YES; 973 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 974 | DEFINES_MODULE = YES; 975 | DYLIB_COMPATIBILITY_VERSION = 1; 976 | DYLIB_CURRENT_VERSION = 1; 977 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 978 | INFOPLIST_FILE = Configs/Stated.plist; 979 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 980 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 981 | PRODUCT_BUNDLE_IDENTIFIER = "com.Stated.Stated-tvOS"; 982 | PRODUCT_NAME = Stated; 983 | SDKROOT = appletvos; 984 | SKIP_INSTALL = YES; 985 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 986 | SWIFT_VERSION = 3.0; 987 | TARGETED_DEVICE_FAMILY = 3; 988 | TVOS_DEPLOYMENT_TARGET = 9.0; 989 | }; 990 | name = Release; 991 | }; 992 | 52D6DA211BF000BD002C0205 /* Debug */ = { 993 | isa = XCBuildConfiguration; 994 | buildSettings = { 995 | APPLICATION_EXTENSION_API_ONLY = YES; 996 | CODE_SIGN_IDENTITY = "-"; 997 | COMBINE_HIDPI_IMAGES = YES; 998 | DEFINES_MODULE = YES; 999 | DYLIB_COMPATIBILITY_VERSION = 1; 1000 | DYLIB_CURRENT_VERSION = 1; 1001 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1002 | FRAMEWORK_VERSION = A; 1003 | INFOPLIST_FILE = Configs/Stated.plist; 1004 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 1005 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 1006 | MACOSX_DEPLOYMENT_TARGET = 10.10; 1007 | PRODUCT_BUNDLE_IDENTIFIER = "com.Stated.Stated-macOS"; 1008 | PRODUCT_NAME = Stated; 1009 | SDKROOT = macosx; 1010 | SKIP_INSTALL = YES; 1011 | SWIFT_VERSION = 3.0; 1012 | }; 1013 | name = Debug; 1014 | }; 1015 | 52D6DA221BF000BD002C0205 /* Release */ = { 1016 | isa = XCBuildConfiguration; 1017 | buildSettings = { 1018 | APPLICATION_EXTENSION_API_ONLY = YES; 1019 | CODE_SIGN_IDENTITY = "-"; 1020 | COMBINE_HIDPI_IMAGES = YES; 1021 | DEFINES_MODULE = YES; 1022 | DYLIB_COMPATIBILITY_VERSION = 1; 1023 | DYLIB_CURRENT_VERSION = 1; 1024 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1025 | FRAMEWORK_VERSION = A; 1026 | INFOPLIST_FILE = Configs/Stated.plist; 1027 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 1028 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 1029 | MACOSX_DEPLOYMENT_TARGET = 10.10; 1030 | PRODUCT_BUNDLE_IDENTIFIER = "com.Stated.Stated-macOS"; 1031 | PRODUCT_NAME = Stated; 1032 | SDKROOT = macosx; 1033 | SKIP_INSTALL = YES; 1034 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 1035 | SWIFT_VERSION = 3.0; 1036 | }; 1037 | name = Release; 1038 | }; 1039 | DD7502831C68FCFC006590AF /* Debug */ = { 1040 | isa = XCBuildConfiguration; 1041 | buildSettings = { 1042 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 1043 | CODE_SIGN_IDENTITY = "-"; 1044 | COMBINE_HIDPI_IMAGES = YES; 1045 | INFOPLIST_FILE = Configs/StatedTests.plist; 1046 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 1047 | MACOSX_DEPLOYMENT_TARGET = 10.11; 1048 | PRODUCT_BUNDLE_IDENTIFIER = "com.Stated.Stated-macOS-Tests"; 1049 | PRODUCT_NAME = "$(TARGET_NAME)"; 1050 | SDKROOT = macosx; 1051 | SWIFT_VERSION = 3.0; 1052 | }; 1053 | name = Debug; 1054 | }; 1055 | DD7502841C68FCFC006590AF /* Release */ = { 1056 | isa = XCBuildConfiguration; 1057 | buildSettings = { 1058 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 1059 | CODE_SIGN_IDENTITY = "-"; 1060 | COMBINE_HIDPI_IMAGES = YES; 1061 | INFOPLIST_FILE = Configs/StatedTests.plist; 1062 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 1063 | MACOSX_DEPLOYMENT_TARGET = 10.11; 1064 | PRODUCT_BUNDLE_IDENTIFIER = "com.Stated.Stated-macOS-Tests"; 1065 | PRODUCT_NAME = "$(TARGET_NAME)"; 1066 | SDKROOT = macosx; 1067 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 1068 | SWIFT_VERSION = 3.0; 1069 | }; 1070 | name = Release; 1071 | }; 1072 | DD7502961C690C7A006590AF /* Debug */ = { 1073 | isa = XCBuildConfiguration; 1074 | buildSettings = { 1075 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 1076 | INFOPLIST_FILE = Configs/StatedTests.plist; 1077 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1078 | PRODUCT_BUNDLE_IDENTIFIER = "com.Stated.Stated-tvOS-Tests"; 1079 | PRODUCT_NAME = "$(TARGET_NAME)"; 1080 | SDKROOT = appletvos; 1081 | SWIFT_VERSION = 3.0; 1082 | TVOS_DEPLOYMENT_TARGET = 9.1; 1083 | }; 1084 | name = Debug; 1085 | }; 1086 | DD7502971C690C7A006590AF /* Release */ = { 1087 | isa = XCBuildConfiguration; 1088 | buildSettings = { 1089 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 1090 | INFOPLIST_FILE = Configs/StatedTests.plist; 1091 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1092 | PRODUCT_BUNDLE_IDENTIFIER = "com.Stated.Stated-tvOS-Tests"; 1093 | PRODUCT_NAME = "$(TARGET_NAME)"; 1094 | SDKROOT = appletvos; 1095 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 1096 | SWIFT_VERSION = 3.0; 1097 | TVOS_DEPLOYMENT_TARGET = 9.1; 1098 | }; 1099 | name = Release; 1100 | }; 1101 | /* End XCBuildConfiguration section */ 1102 | 1103 | /* Begin XCConfigurationList section */ 1104 | 52D6D9761BEFF229002C0205 /* Build configuration list for PBXProject "Stated" */ = { 1105 | isa = XCConfigurationList; 1106 | buildConfigurations = ( 1107 | 52D6D98E1BEFF229002C0205 /* Debug */, 1108 | 52D6D98F1BEFF229002C0205 /* Release */, 1109 | ); 1110 | defaultConfigurationIsVisible = 0; 1111 | defaultConfigurationName = Release; 1112 | }; 1113 | 52D6D9901BEFF229002C0205 /* Build configuration list for PBXNativeTarget "Stated-iOS" */ = { 1114 | isa = XCConfigurationList; 1115 | buildConfigurations = ( 1116 | 52D6D9911BEFF229002C0205 /* Debug */, 1117 | 52D6D9921BEFF229002C0205 /* Release */, 1118 | ); 1119 | defaultConfigurationIsVisible = 0; 1120 | defaultConfigurationName = Release; 1121 | }; 1122 | 52D6D9931BEFF229002C0205 /* Build configuration list for PBXNativeTarget "Stated-iOS Tests" */ = { 1123 | isa = XCConfigurationList; 1124 | buildConfigurations = ( 1125 | 52D6D9941BEFF229002C0205 /* Debug */, 1126 | 52D6D9951BEFF229002C0205 /* Release */, 1127 | ); 1128 | defaultConfigurationIsVisible = 0; 1129 | defaultConfigurationName = Release; 1130 | }; 1131 | 52D6D9E71BEFFF6E002C0205 /* Build configuration list for PBXNativeTarget "Stated-watchOS" */ = { 1132 | isa = XCConfigurationList; 1133 | buildConfigurations = ( 1134 | 52D6D9E81BEFFF6E002C0205 /* Debug */, 1135 | 52D6D9E91BEFFF6E002C0205 /* Release */, 1136 | ); 1137 | defaultConfigurationIsVisible = 0; 1138 | defaultConfigurationName = Release; 1139 | }; 1140 | 52D6DA011BEFFFBE002C0205 /* Build configuration list for PBXNativeTarget "Stated-tvOS" */ = { 1141 | isa = XCConfigurationList; 1142 | buildConfigurations = ( 1143 | 52D6DA021BEFFFBE002C0205 /* Debug */, 1144 | 52D6DA031BEFFFBE002C0205 /* Release */, 1145 | ); 1146 | defaultConfigurationIsVisible = 0; 1147 | defaultConfigurationName = Release; 1148 | }; 1149 | 52D6DA201BF000BD002C0205 /* Build configuration list for PBXNativeTarget "Stated-macOS" */ = { 1150 | isa = XCConfigurationList; 1151 | buildConfigurations = ( 1152 | 52D6DA211BF000BD002C0205 /* Debug */, 1153 | 52D6DA221BF000BD002C0205 /* Release */, 1154 | ); 1155 | defaultConfigurationIsVisible = 0; 1156 | defaultConfigurationName = Release; 1157 | }; 1158 | DD7502821C68FCFC006590AF /* Build configuration list for PBXNativeTarget "Stated-macOS Tests" */ = { 1159 | isa = XCConfigurationList; 1160 | buildConfigurations = ( 1161 | DD7502831C68FCFC006590AF /* Debug */, 1162 | DD7502841C68FCFC006590AF /* Release */, 1163 | ); 1164 | defaultConfigurationIsVisible = 0; 1165 | defaultConfigurationName = Release; 1166 | }; 1167 | DD7502951C690C7A006590AF /* Build configuration list for PBXNativeTarget "Stated-tvOS Tests" */ = { 1168 | isa = XCConfigurationList; 1169 | buildConfigurations = ( 1170 | DD7502961C690C7A006590AF /* Debug */, 1171 | DD7502971C690C7A006590AF /* Release */, 1172 | ); 1173 | defaultConfigurationIsVisible = 0; 1174 | defaultConfigurationName = Release; 1175 | }; 1176 | /* End XCConfigurationList section */ 1177 | }; 1178 | rootObject = 52D6D9731BEFF229002C0205 /* Project object */; 1179 | } 1180 | --------------------------------------------------------------------------------