{"id":32324462,"url":"https://github.com/mrtksn/swiftystate","last_synced_at":"2026-02-20T23:02:15.223Z","repository":{"id":62457002,"uuid":"196277998","full_name":"mrtksn/SwiftyState","owner":"mrtksn","description":"Swift State Engine","archived":false,"fork":false,"pushed_at":"2019-07-25T08:20:07.000Z","size":91,"stargazers_count":6,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-12-07T07:14:50.553Z","etag":null,"topics":["device-debugger","independent-components","state-engine","state-machine","state-management","swift-library","uikit","xcode"],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mrtksn.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-07-10T21:35:39.000Z","updated_at":"2023-06-04T05:23:01.000Z","dependencies_parsed_at":"2022-11-02T00:15:15.078Z","dependency_job_id":null,"html_url":"https://github.com/mrtksn/SwiftyState","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/mrtksn/SwiftyState","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrtksn%2FSwiftyState","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrtksn%2FSwiftyState/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrtksn%2FSwiftyState/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrtksn%2FSwiftyState/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mrtksn","download_url":"https://codeload.github.com/mrtksn/SwiftyState/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrtksn%2FSwiftyState/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29667119,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-20T19:49:36.704Z","status":"ssl_error","status_checked_at":"2026-02-20T19:44:05.372Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["device-debugger","independent-components","state-engine","state-machine","state-management","swift-library","uikit","xcode"],"created_at":"2025-10-23T13:04:55.894Z","updated_at":"2026-02-20T23:02:15.217Z","avatar_url":"https://github.com/mrtksn.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SwiftyState\n\n### v1.1.1\n\nSwiftyState is a **State Engine** featuring a powerful **on-device debugger with a graphical user interface**. You can also do time travel in your app. Inspired by the Redux JS Library but not too much.\n\n*Here is a video preview of the Demo App. The UIKit elements automatically update as the state changes. The interface at the bottom is the on-device debugger where the developer can view and undo state changes.*\n[![SwiftyState Demo App](http://img.youtube.com/vi/TwmqQTe3K58/0.jpg)](http://www.youtube.com/watch?v=TwmqQTe3K58)\n\n### What is it for?\n\nIn SwiftState  you create a *central source of truth*, a *state*, which is a fancy way to say a *struct* that keeps your app data. You can modify that data only by calling actions and you subscribe to get notified when someting has changed.\n\nThis enables you to create independent components that simply observe the state and act accordingly without worrying about the other parts of the App.  Having all your data in one place simplifies verifying it's validity and management.  \n\nFor example, how do you persist your App state between sessions? With SwiftyState you don't have to do almost anything, you simply call save() or load() to save and load the state from the disk and it's done. Don't want to to keep your data on disk want it online? Easy, SwiftState exports and imports JSON  so you have the state of your app anywhere.\n\nHaving a graphical debugger that runs on iOS will also help you to hunt bugs, understand quirks or admire the internals of your creation even when you do not have XCode with you. Simply shake your device and the debugger will pop up(If you choose to).\n\n### The on-device state debugger\n\nSwiftyState comes with a debugger that runs on iOS and can be launced by shaking the device(this can be changed). It is an standalone UI where you can view the current state, the actions and you can roll back to a previous state - time travel.\n\n## Example\n\nFirst thing first, you need to import SwiftyState\n\n```Swift\nimport SwiftyState\n```\n\n#### Create your state object\n\nIn SwiftyState, you need to define your state structure. You have only one of these.\n\n```Swift\n/// This is your store. It is a struct that conforms to the SwiftyStateStore\nstruct MyStore : SwiftyStateStoreEquatable {\n    var Jenny : Int = 100\n    var Donald : Int = 20\n}\n/// Add this to make SwiftState return your store when asked\nextension SwiftyState {\n    func getState()-\u003eMyStore{\n        return self.getRawState() as! MyStore\n    }\n}\n```\n\nSo by default Jenny has 100 dollars and Donald only 20. These values are available anywhere in your app. You can access them getting the latest copy of your state\n\n```Swift\n/// Get a copy of your state\nlet state = SwiftyState().getState()\n\nprint(\"Jenny owns \\(state.Jenny) and Donald owns \\(state.Donald)\")\n/// the output is \"Jenny owns 100 and Donald owns 20\"\n```\n\n#### Create actions\n\nHow do you change state? You create actions and call these actions to correctly modify your data.\n\nHere is an action to make Jenny give Donald 10 bucks. You simply conform to SwiftyAction protocol with an enum, add your actions as cases and write the logic of the action inside the reducer function.\n\nDon't worry, it's autocomplete friendly. XCode(or your favorite IDE) will help you out so you don't have to write too much code.\n\n```Swift\nenum PayAction : SwiftyAction {\n    case give10BuckToDonald\n\n    func reducer(state: SwiftyStateStore) -\u003e SwiftyStateStore {\n        var newState = state as! MyStore\n        switch self {\n        case .give10BuckToDonald:\n            newState.Donald += 10\n            newState.Jenny -= 10\n        }\n        // When you are done changing, always return the new state\n        return newState\n    }\n}\n```\n\nAnywhere in your app you can call this action and Jenny will give Donald 10 dollars. You call actions like this:\n\n```Swift\nSwiftyState().action(a: PayAction.give10BuckToDonald)\n```\n\nOkay, how about giving custom amount? Let's add another action\n\n```Swift\nenum PayAction : SwiftyAction {\n    case give10BuckToDonald\n    case giveToDonald(amount : Int)\n\n    func reducer(state: SwiftyStateStore) -\u003e SwiftyStateStore {\n        var newState = state as! MyStore\n        switch self {\n        case .give10BuckToDonald:\n            newState.Donald += 10\n            newState.Jenny -= 10\n        case .giveToDonald(let amount):\n            newState.Donald += amount\n            newState.Jenny -= amount\n        }\n        // When you are done changing, always return the new state\n        return newState\n    }\n}\n```\n\nThen call that action from anywhere\n\n```Swift\nSwiftyState().action(a: PayAction.giveToDonald(amount: 5))\n```\n\nDo you see the pattern here? You can keep adding actions as you need and it doesn't have to be in one place.  For example, let's create a StealAction that will steal from Donald and Jenny\n\n```Swift\nenum StealAction : SwiftyAction{\n    case stealAll\n\n    func reducer(state: SwiftyStateStore) -\u003e SwiftyStateStore {\n        var newState = state as! MyStore\n        newState.Donald = 0\n        newState.Jenny = 0\n        return newState\n    }\n}\n```\n\nCall from anywhere\n\n```Swift\nSwiftyState().action(a: StealAction.stealAll)\n```\n\n#### Listen to changes\n\nGood way to have independent components is to make them aware of your data and adapt as the data changes. You do that by subscribing to state changes, like this:\n\n```Swift\nlet subscription = SwiftyState().subscribe { [weak self] in\n    let state = $0 as! MyStore\n    let oldState = $1 as? MyStore\n\n    /// Your code goes here\n}\n```\n\nThe `SwiftyState().subscribe()` method return a  `SwiftySubscription` object that holds an ID and an unsubscribe method. If your object is no longer needed, do not forget to unsubscribe to prevent memory leaks.\n\nFor example, when you are using SwiftState with UIKit, the best place to subscribe to changes is in the viewDidLoad() for a ViewController and unsubscribe in the deinit(). Let's have a look:\n\n```Swift\nclass MoneyStatus : UIViewController {\n    var subscription : SwiftySubscription?\n\n    override func viewDidLoad() {\n        self.subscription = SwiftyState().subscribe { [weak self] in\n            let state = $0 as! MyStore // the new state\n            let oldState = $1 as? MyStore // the old state\n            // if the state changed, apply the changes\n            if state.Donald != oldState?.Donald{\n                print(\"Donald now owns \\(state.Donald)$\")\n            }\n        }\n        // execute the closure at the start\n        self.subscription?.hotStart()\n    }\n\n    // free up resources when you no longer need it\n    deinit {\n        self.subscription?.unsubscribe()\n    }\n}\n```\n\n#### Validate changes\n\nWith SwiftyState you have the option to reject action results if you are not happy with the results. For example, let's make sure that Jenny and Donald do not acquire debt.\n\n```Swift\nstruct MyValidator : SwiftyStateValidiator{\n    func validiator(_ state: SwiftyStateStore) -\u003e Bool {\n        // the new state is available here, before making it available everywhere:\n        let newState = state as! MyStore\n\n        return (newState.Donald \u003e= 0) \u0026\u0026 (newState.Jenny \u003e= 0)\n    }\n}\n```\n\nIt's quite simple really. You create a struct that conforms to `SwiftyStateValidiator` which means that you will have a validiator function that receives the new state every time an action is run. You evaluate the new state and return `true` if it is O.K. or return `false` if not. If you return `false`, the new state is discarded and the old state is passed.\n\n#### Keep In Mind\n\nSwiftyState runs synchronously on the main thread. This means that if you do heavy calculations inside the actions or subscriptions the app may freeze until your code ise done. Keep the actions pure(that is, only modify the state using the data available for the reducer, no async inside the actions) and if you have heavy calculations, use SwiftState subscriptions to initiate them asynchronously or in seperate treads.\n\nAn exaple app is included, please take a look.\n\nTo run the example project, clone the repo, and run `pod install` from the Example directory first.\n\n## Installation\n\n#### get SwiftState\n\nSwiftyState is available through [CocoaPods](https://cocoapods.org). To install\nit, simply add the following line to your Podfile:\n\n```ruby\npod 'SwiftyState'\n```\n\n#### Integrate into your project\n\nAs with any framework, import SwiftState wherever you use SwiftyState\n\n```Swift\nimport SwiftyState\n```\n\nCreate your State and Validiator objects as described. Keeping them in a seperate Swift file is usually good idea.\n\nInitiate the SwiftyState in the `didFinishLaunchingWithOptions` method, in your `AppDelegate.swift`. It is recommended to save and load the state from disk to keep your data between sessions.\n\n```Swift\nfunc application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -\u003e Bool {\n    // Override point for customization after application launch.\n    //Configure SwiftyState to use your State object\n    SwiftyState().setStore(MyStore())\n    // Configure SwiftState to use your validator\n    SwiftyState().setStateValidiator(MyValidator())\n    // Load the state from the disk\n    SwiftyState().load()\n    // Start collecting state history for debugging. Remove this one before shipping\n    SwiftyState().startDebug()\n    return true\n}\n\n/// Save the state before app terminates. You can save the state whenever you like.\nfunc applicationWillTerminate(_ application: UIApplication) {\n    SwiftyState().save()\n}\n```\n\nThis is enough to use SwiftyState hovever if you want to use the debugger, you need to make a way to launch it. The recommended way is to listen for device shake. To do this add the following line to your main ViewController.\n\n```Swift\noverride func viewDidLoad() {\n    super.viewDidLoad()\n    // Enable SwiftyState Debuggur UI to be displayed when you shake your device.\n    // Remove this one before shipping unless you want to expose the debugger to your users\n    SwiftyState().debugUIManager().showOnShake(self)\n\n    /// --------- the rest of your code ---- ///\n}\n```\n\nIf you do not wish to use the device shaking, you can launch the debugger by calling `SwiftyState().debugUIManager().showDebugger(self)`\n\n## Author\n\nMertol Kasanan, mrtksn at gmail\n\n## License\n\nSwiftyState is available under the MIT license. See the LICENSE file for more info.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmrtksn%2Fswiftystate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmrtksn%2Fswiftystate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmrtksn%2Fswiftystate/lists"}