{"id":929,"url":"https://github.com/JohnSundell/Flow","last_synced_at":"2025-07-30T19:33:06.487Z","repository":{"id":56911475,"uuid":"45642999","full_name":"JohnSundell/Flow","owner":"JohnSundell","description":"Operation Oriented Programming in Swift","archived":false,"fork":false,"pushed_at":"2017-01-26T23:07:36.000Z","size":53,"stargazers_count":216,"open_issues_count":1,"forks_count":6,"subscribers_count":9,"default_branch":"master","last_synced_at":"2024-11-27T12:25:40.525Z","etag":null,"topics":["async","operations","swift"],"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/JohnSundell.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":"2015-11-05T22:17:17.000Z","updated_at":"2024-08-27T07:52:30.000Z","dependencies_parsed_at":"2022-08-21T03:20:15.859Z","dependency_job_id":null,"html_url":"https://github.com/JohnSundell/Flow","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JohnSundell%2FFlow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JohnSundell%2FFlow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JohnSundell%2FFlow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JohnSundell%2FFlow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JohnSundell","download_url":"https://codeload.github.com/JohnSundell/Flow/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228179016,"owners_count":17881123,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["async","operations","swift"],"created_at":"2024-01-05T20:15:34.862Z","updated_at":"2024-12-04T19:32:21.355Z","avatar_url":"https://github.com/JohnSundell.png","language":"Swift","funding_links":[],"categories":["Concurrency"],"sub_categories":["Linter","Other free courses"],"readme":"# Flow\n\n![Travis](https://img.shields.io/travis/JohnSundell/Flow/master.svg)\n![CocoaPods](https://img.shields.io/cocoapods/v/FlowOperations.svg)\n[![Carthage](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)\n\nFlow is a lightweight Swift library for doing operation oriented programming. It enables you to easily define your own, atomic operations, and also contains an exensive library of ready-to-use operations that can be grouped, sequenced, queued and repeated.\n\n## Operations\n\nUsing Flow is all about splitting your code up into multiple atomic pieces - called **operations**. Each operation defines a body of work, that can easily be reused throughout an app or library.\n\nAn operation can do anything, synchronously or asynchronously, and its scope is really up to you. The true power of operation oriented programming however, comes when you create groups, sequences and queues out of operations. Operations can potentially make code that is either asynchronous, or where work has to be done in several places, a lot simpler.\n\n## How to use\n\n- Create your own operations by conforming to `FlowOperation` in a custom object. All it needs to do it implement one method that performs it with a completion handler. It’s free to be initialized in whatever way you want, and can be either a `class` or a `struct`.\n\n- Use any of the built-in operations, such as `FlowClosureOperation`, `FlowDelayOperation`, etc.\n\n- Create sequences of operations (that get executed one by one) using `FlowOperationSequence`, groups (that get executed all at once) using `FlowOperationGroup`, or queues (that can be continuously filled with operations) using `FlowOperationQueue`.\n\n## Example\n\nLet’s say we’re building a game and we want to perform a series of animations where a `Player` attacks an `Enemy`, destroys it and then plays a victory animation. This could of course be accomplished with the use of completion handler closures:\n\n```swift\nplayer.moveTo(enemy.position) {\n    player.performAttack() {\n        enemy.destroy() {\n            player.playVictoryAnimation()\n        }\n    }\n}\n```\n\nHowever, this quickly becomes hard to reason about and debug, especially if we start adding multiple animations that we want to sync. Let’s say we decide to implement a new **spin attack** in our game, that destroys multiple enemies, and we want all enemies to be destroyed before we play the victory animation. We’d have to do something like this:\n\n```swift\nplayer.moveTo(mainEnemy.position) {\n    player.performAttack() {\n        var enemiesDestroyed = 0\n                \n        for enemy in enemies {\n            enemy.destroy({\n                enemiesDestroyed += 1\n                        \n                if enemiesDestroyed == enemies.count {\n                    player.playVictoryAnimation()\n                }\n            })\n        }\n    }\n}\n```\n\nIt becomes clear that the more we add to our animation, the more error prone and hard to debug it becomes. Wouldn’t it be great if our animations (or any other sequence of tasks) could scale gracefully as we make them more and more complex?\n\nLet’s implement the above using Flow instead. We’ll start by defining all tasks that we need to perform during our animation as **operations**:\n\n```swift\n/// Operation that moves a Player to a destination\nclass PlayerMoveOperation: FlowOperation {\n    private let player: Player\n    private let destination: CGPoint\n    \n    init(player: Player, destination: CGPoint) {\n        self.player = player\n        self.destination = destination\n    }\n    \n    func perform(completionHandler: @escaping () -\u003e Void) {\n        self.player.moveTo(self.destination, completionHandler: completionHandler)\n    }\n}\n\n/// Operation that performs a Player attack\nclass PlayerAttackOperation: FlowOperation {\n    private let player: Player\n    \n    init(player: Player) {\n        self.player = player\n    }\n    \n    func perform(completionHandler: @escaping () -\u003e Void) {\n        self.player.performAttack(completionHandler)\n    }\n}\n\n/// Operation that destroys an enemy\nclass EnemyDestroyOperation: FlowOperation {\n    private let enemy: Enemy\n    \n    init(enemy: Enemy) {\n        self.enemy = enemy\n    }\n    \n    func perform(completionHandler: @escaping () -\u003e Void) {\n        self.enemy.destroy(completionHandler)\n    }\n}\n\n/// Operation that plays a Player victory animation\nclass PlayerVictoryOperation: FlowOperation {\n    private let player: Player\n    \n    init(player: Player) {\n        self.player = player\n    }\n    \n    func perform(completionHandler: @escaping () -\u003e Void) {\n        self.player.playVictoryAnimation()\n        completionHandler()\n    }\n}\n```\n\nSecondly; we’ll implement our animation using the above operations:\n\n```swift\nlet moveOperation = PlayerMoveOperation(player: player, destination: mainEnemy.position)\nlet attackOperation = PlayerAttackOperation(player: player)\nlet destroyEnemiesOperation = FlowOperationGroup(operations: enemies.map({\n    return EnemyDestroyOperation(enemy: $0)\n}))\nlet victoryOperation = PlayerVictoryOperation(player: player)\n        \nlet operationSequence = FlowOperationSequence(operations: [\n    moveOperation,\n    attackOperation,\n    destroyEnemiesOperation,\n    victoryOperation\n])\n        \noperationSequence.perform()\n```\n\nWhile we had to write a bit more code using operations; this approach has some big advantages.\n\nFirstly; we can now use a `FlowOperationGroup` to make sure that all enemy animations are finished before moving on, and by doing this we’ve reduced the state we need to keep within the animation itself.\n\nSecondly; all parts of the animation are now independant operations that don’t have to be aware of each other, making them a lot easier to test \u0026 debug - and they could also be reused in other parts of our game.\n\n## API reference\n\n### Protocols\n\n**`FlowOperation`**\nUsed to declare custom operations.\n\n**`FlowOperationCollection`**\nUsed to declare custom collections of operations.\n\n### Base operations\n\n**`FlowClosureOperation`**\nOperation that runs a closure, and returns directly when performed.\n\n**`FlowAsyncClosureOperation`**\nOperation that runs a closure, then waits for that closure to call a completion handler before it finishes.\n\n**`FlowDelayOperation`**\nOperation that waits for a certain delay before finishing. Useful in sequences and queues.\n\n### Operation collections \u0026 utilities\n\n**`FlowOperationGroup`**\nUsed to group together a series of operations that all get performed at once when the group is performed.\n\n**`FlowOperationSequence`**\nUsed to sequence a series of operations, performing them one by one once the sequence is performed.\n\n**`FlowOperationQueue`**\nQueue that keeps executing the next operation as soon as it becomes idle. New operations can constantly be added.\n\n**`FlowOperationRepeater`**\nUsed to repeat operations, optionally using an interval in between repeats.\n\n## How is this different from NSOperations?\n\n`NSOperations` are awesome - and are definetly one of the main sources of inspiration for Flow. However, `NSOperations` are quite heavyweight and can potentially take a long time to implement. Flow was designed to have the power of `NSOperations`, but be a lot easier to use. It’s also written 100% using Swift - making it ideal for Swift-based projects.\n\n## Compatibility\n\nFlow supports all current Apple platforms with the following minimum versions:\n\n- iOS 8\n- macOS 10.11\n- watchOS 2\n- tvOS 9\n\nThe current version of Flow supports Swift 3. If you need Swift 2 support, either use [version 1.1](https://github.com/JohnSundell/Flow/releases/tag/1.1), or the [`swift 2` branch](https://github.com/JohnSundell/Flow/tree/swift2).\n\n## Installation\n\n**CocoaPods:**\n\nAdd the line `pod \"FlowOperations\"` to your `Podfile`\n\n**Carthage:**\n\nAdd the line `github \"johnsundell/flow\"` to your `Cartfile`\n\n**Manual:**\n\nClone the repo and drag the file `Flow.swift` into your Xcode project.\n\n**Swift Package Manager:**\n\nAdd the line `.Package(url: \"https://github.com/johnsundell/flow.git\", majorVersion: 2)` to your `Package.swift`\n\n## Hope you enjoy using Flow!\n\nFor support, feedback \u0026 news about Flow; follow me on Twitter: [@johnsundell](http://twitter.com/johnsundell).\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FJohnSundell%2FFlow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FJohnSundell%2FFlow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FJohnSundell%2FFlow/lists"}