{"id":19011891,"url":"https://github.com/3squared/peakoperation","last_synced_at":"2025-04-22T23:27:57.954Z","repository":{"id":46535200,"uuid":"149304926","full_name":"3Squared/PeakOperation","owner":"3Squared","description":"PeakOperation is a Swift microframework providing enhancement and conveniences to Operation.","archived":false,"fork":false,"pushed_at":"2021-12-01T10:45:54.000Z","size":245,"stargazers_count":10,"open_issues_count":0,"forks_count":4,"subscribers_count":7,"default_branch":"develop","last_synced_at":"2025-04-10T20:36:10.363Z","etag":null,"topics":["asynchronous","nsoperation","operation","peak-framework","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/3Squared.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-09-18T14:47:46.000Z","updated_at":"2023-04-09T16:22:59.000Z","dependencies_parsed_at":"2022-09-14T07:32:52.149Z","dependency_job_id":null,"html_url":"https://github.com/3Squared/PeakOperation","commit_stats":null,"previous_names":[],"tags_count":25,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/3Squared%2FPeakOperation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/3Squared%2FPeakOperation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/3Squared%2FPeakOperation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/3Squared%2FPeakOperation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/3Squared","download_url":"https://codeload.github.com/3Squared/PeakOperation/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250339511,"owners_count":21414367,"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":["asynchronous","nsoperation","operation","peak-framework","swift"],"created_at":"2024-11-08T19:16:04.519Z","updated_at":"2025-04-22T23:27:57.934Z","avatar_url":"https://github.com/3Squared.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Peak Operation](PeakOperation.png \"Peak Operation\")\n\nPeakOperation is a Swift microframework providing enhancement and conveniences to [`Operation`](https://developer.apple.com/documentation/foundation/operation). It is part of the [Peak Framework](#peak-framework).\n\n## Concurrent Operations\n\n`ConcurrentOperation` is an abstract `Operation` subclass that can perform work asynchronously. You override `execute()` to perform your work, and when it is completed call `finish()` to complete the operation.\n\n```swift\nclass MyOperation: ConcurrentOperation {\n   override func execute() {\n        print(\"Hello World!\")\n        finish()\n    }\n}\n\nlet queue = OperationQueue()\nlet operation = MyOperation()\noperation.enqueue(on: queue)\n\n```\n\nThis means that you can perform asynchronous work inside `execute()`, such as performing a `URLSession` request. \n\n## Chaining operations\n\n`Operation` provides the ability to add dependencies between operations. PeakOperation builds upon this functionality and wraps it in an easy-to-use API.\n\n```swift\nlet firstOperation = ...\nlet secondOperation = ...\n        \nfirstOperation\n    .then(do: secondOperation)\n    .enqueue()\n```\n\nIn the example above, `secondOperation` will run once `firstOperation` finishes.\n\nYou can also call `enqueueWithProgress()` on a chain of operations or `overallProgress()` on a single operation to track their progress.\n\n```swift\nlet progress: Progress = firstOperation\n    .then(do: secondOperation)\n    .enqueueWithProgress()\n\n// or\n\nlet progress: Progress = secondOperation.overallProgress()\n```\n\n## Passing Results\n\nAdding dependencies between operations is useful, but passing results between operations is where PeakOperation really shines. PeakOperation includes two protocols your operation can conform to: `ProducesResult` and `ConsumesResult`.\n\nLet's say we have three operations. The first produces a `Result\u003cInt, Error\u003e`.\n\n```swift\nclass IntOperation: ConcurrentOperation, ProducesResult {\n    var output: Result\u003cInt, Error\u003e\n    override func execute() {\n        output = Result { 1 }\n        finish()\n    }\n}\n```\n\nThe second operation consumes a `Result\u003cInt, Error\u003e` and produces a `Result\u003cString, Error\u003e`. It unpacks its input, adds 1, converts it to a string, then sets it's output.\n\n```swift\nclass AddOneOperation: ConcurrentOperation, ConsumesResult, ProducesResult {\n    var input: Result\u003cInt, Error\u003e\n    var output: Result\u003cString, Error\u003e\n    override func execute() {\n        output = Result { \"\\(try input.get() + 1)\" }\n        finish()\n    }\n}\n```\n\nThe final operation consumes a `Result\u003cString, Error\u003e`. It unpacks it and prints it to the console:\n\n```swift\nclass PrintOperation: ConcurrentOperation, ConsumesResult {\n    var input: Result\u003cString, Error\u003e\n    override func execute() {\n        do {\n            print(\"Hello \\(try input.get())!\")\n        } catch { }\n        finish()\n    }\n}\n```\n\nUsing `passesResult(to:)`, these three operations can be chained together!\n\n```swift\nIntOperation()\n    .passesResult(to: AddOneOperation())\n    .passesResult(to: PrintOperation())\n    .enqueue()\n\n    // Hello 2!\n```\n\nAs long as the input type matches the output type, you can pass results between any operations conforming to the protocols.\n\nIf any of the operations fail and its result is `.failure`, then the result will still be passed into the next operation. It's up to you to unwrap the result and deal with the error appropriately, perhaps by rethrowing the error.\n\n```swift\nclass RethrowingOperation: ConcurrentOperation, ConsumesResult, ProducesResult {\n    var input: Result\u003cString, Error\u003e\n    var output: Result\u003cString, Error\u003e\n    override func execute() {\n        do {\n            let _ = try input.get()\n            output = ...\n        } catch { \n            output = Result { throw error }\n        }\n        finish()\n    }\n}\n```\n\nThat way, any of the operations can fail and you can still retrieve the error at the end.\n\n```swift\nlet failingOperation = ...\nlet successfulOperation = ...\nlet anotherSuccessfulOperation = ...\n\nfailingOperation\n    .passesResult(to: successfulOperation)\n    .passesResult(to: anotherSuccessfulOperation)\n    .enqueue()\n\nanotherSuccessfulOperation.addResultBlock { result in\n    // result would contain the error from failingOperation \n    // even though the other operations still ran\n}\n```\n\n## Grouping\n\n`GroupChainOperation` takes an operation and its dependants and executes them on an internal queue. The result of the operations is retained and it is inspected in order that this operation can produce a result of type ` Result\u003cVoid, Error\u003e` - the value is lost, but the `.success`/`.failure` is kept. This allows you to chain together groups with otherwise incompatible input/outputs.\n\n```swift\n// would otherwise produce String, now produces Void\nlet group1 = intOperation\n    .passesResult(to: stringOperation)\n    .group()\n\n// Would otherwise accept Bool, now consumes Void\nlet group2 = boolOperation\n    .passesResult(to: anyOperation)\n    .group()\n\ngroup1\n    .passesResult(to: group2)\n    .enqueue()\n\n```\n\n## Retrying\n\nSometimes an operation might fail. Perhaps you are dealing with a flaky web service or connection. For this, you can subclass `RetryingOperation`. This is an operation which `ProducesResult`, and if the result is `.failure`, it will try again using a given `retryStrategy` closure.\n\n```swift\nclass MyRetryingOperation: RetryingOperation\u003cAnyObject\u003e {\n   override func execute() {\n        output = Result { throw error }\n        finish()\n    }\n}\n\nlet operation = MyRetryingOperation()\noperation.retryStrategy = { failureCount in\n    return failureCount \u003c 3\n}\n```\n\nYou can provide your own block as a `retryStrategy`. Here, the operation will be run 3 times before it finally fails.\n\nThere are 2 provided `StrategyBlocks`: \n\n- `RetryStrategy.none`\n- `RetryStrategy.repeat(times: Int)`\n\n## Examples\n\nPlease see the included tests for further examples. Also check out [PeakNetwork]() which uses PeakOperation extensively. \n\n## Contributing\n\nPlease read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us.\n\n## Versioning\n\nWe use [SemVer](http://semver.org/) for versioning.\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details\n\n## Acknowledgments\n\n- [calebd/AsynchronousOperation.swift](https://gist.github.com/calebd/93fa347397cec5f88233)\n- [ProcedureKit](https://github.com/ProcedureKit/ProcedureKit)\n\n# Peak Framework\n\nThe Peak Framework is a collection of open-source microframeworks created by the team at [3Squared](https://github.com/3squared), named for the [Peak District](https://en.wikipedia.org/wiki/Peak_District). It is made up of:\n\n|Name|Description|\n|:--|:--|\n|[PeakCoreData](https://github.com/3squared/PeakCoreData)|Provides enhances and conveniences to `Core Data`.|\n|[PeakNetwork](https://github.com/3squared/PeakNetwork)|A networking framework built on top of `Session` using PeakOperation, leveraging the power of `Codable`.|\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F3squared%2Fpeakoperation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F3squared%2Fpeakoperation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F3squared%2Fpeakoperation/lists"}