{"id":1096,"url":"https://github.com/ReactKit/SwiftTask","last_synced_at":"2025-07-30T20:31:13.379Z","repository":{"id":19989427,"uuid":"23256663","full_name":"ReactKit/SwiftTask","owner":"ReactKit","description":"Promise + progress + pause + cancel + retry for Swift.","archived":false,"fork":false,"pushed_at":"2019-04-15T03:23:50.000Z","size":574,"stargazers_count":1934,"open_issues_count":5,"forks_count":176,"subscribers_count":74,"default_branch":"swift/4.0","last_synced_at":"2024-10-29T17:56:18.600Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","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/ReactKit.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":"2014-08-23T13:39:12.000Z","updated_at":"2024-10-13T20:09:52.000Z","dependencies_parsed_at":"2022-07-22T03:32:00.845Z","dependency_job_id":null,"html_url":"https://github.com/ReactKit/SwiftTask","commit_stats":null,"previous_names":[],"tags_count":29,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ReactKit%2FSwiftTask","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ReactKit%2FSwiftTask/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ReactKit%2FSwiftTask/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ReactKit%2FSwiftTask/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ReactKit","download_url":"https://codeload.github.com/ReactKit/SwiftTask/tar.gz/refs/heads/swift/4.0","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":222136320,"owners_count":16937396,"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":[],"created_at":"2024-01-05T20:15:38.833Z","updated_at":"2024-12-04T19:32:50.563Z","avatar_url":"https://github.com/ReactKit.png","language":"Swift","funding_links":[],"categories":["EventBus","Swift","Extensions","Utilities and Extensions"],"sub_categories":["Getting Started","Other free courses","Linter"],"readme":"SwiftTask [![Circle CI](https://circleci.com/gh/ReactKit/SwiftTask/tree/swift%2F2.0.svg?style=svg)](https://circleci.com/gh/ReactKit/SwiftTask/tree/swift%2F2.0)\n=========\n\n[Promise](http://www.html5rocks.com/en/tutorials/es6/promises/) + progress + pause + cancel + retry for Swift.\n\n![SwiftTask](Screenshots/diagram.png)\n\n\n## How to install\n\nSee [ReactKit Wiki page](https://github.com/ReactKit/ReactKit/wiki/How-to-install).\n\n\n## Example\n\n### Basic\n\n```swift\n// define task\nlet task = Task\u003cFloat, String, NSError\u003e { progress, fulfill, reject, configure in\n\n    player.doSomethingWithProgress({ (progressValue: Float) in\n        progress(progressValue) // optional\n    }, completion: { (value: NSData?, error: NSError?) in\n        if error == nil {\n            fulfill(\"OK\")\n        }\n        else {\n            reject(error)\n        }\n    })\n\n    // pause/resume/cancel configuration (optional)\n    configure.pause = { [weak player] in\n        player?.pause()\n    }\n    configure.resume = { [weak player] in\n        player?.resume()\n    }\n    configure.cancel = { [weak player] in\n        player?.cancel()\n    }\n\n}\n\n// set success \u0026 failure\ntask.success { (value: String) -\u003e Void in\n    // do something with fulfilled value\n}.failure { (error: NSError?, isCancelled: Bool) -\u003e Void in\n    // do something with rejected error\n}\n\n// you can call configured operations outside of Task-definition\ntask.pause()\ntask.resume()\ntask.cancel()\n```\n\nNotice that `player` has following methods, which will work nicely with `SwiftTask`:\n\n- `doSomethingWithProgress(_:completion:)` (progress callback as optional)\n- `pause()` (optional)\n- `resume()` (optional)\n- `cancel()` (optional)\n\nOne of the best example would be [Alamofire](https://github.com/Alamofire/Alamofire) (networking library)\n as seen below.\n\n### Using [Alamofire](https://github.com/Alamofire/Alamofire)\n\n```swift\ntypealias Progress = (bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64)\ntypealias AlamoFireTask = Task\u003cProgress, String, NSError\u003e\n\n// define task\nlet task = AlamoFireTask { progress, fulfill, reject, configure in\n\n    Alamofire.download(.GET, \"http://httpbin.org/stream/100\", destination: somewhere)\n    .progress { bytesWritten, totalBytesWritten, totalBytesExpectedToWrite in\n\n        progress((bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) as Progress)\n\n    }.response { request, response, data, error in\n\n        if let error = error {\n            reject(error)\n            return\n        }\n\n        fulfill(\"OK\")\n\n    }\n\n    return\n}\n\n// set progress \u0026 then\ntask.progress { (oldProgress: Progress?, newProgress: Progress) in\n\n    println(\"\\(newProgress.bytesWritten)\")\n    println(\"\\(newProgress.totalBytesWritten)\")\n    println(\"\\(newProgress.totalBytesExpectedToWrite)\")\n\n}.then { (value: String?, errorInfo: AlamoFireTask.ErrorInfo?) -\u003e Void in\n    // do something with fulfilled value or rejected errorInfo\n}\n```\n\n### Retry-able\n\n`Task` can retry for multiple times by using `retry()` method.\nFor example, `task.retry(n)` will retry at most `n` times (total tries = `n+1`) if `task` keeps rejected, and `task.retry(0)` is obviously same as `task` itself having no retries.\n\nThis feature is extremely useful for unstable tasks e.g. network connection.\nBy implementing *retryable* from `SwiftTask`'s side, similar code is no longer needed for `player` (inner logic) class.\n\n```swift\ntask.retry(2).progress { ... }.success { ...\n    // this closure will be called even when task is rejected for 1st \u0026 2nd try\n    // but finally fulfilled in 3rd try.\n}\n```\n\nFor more examples, please see XCTest cases.\n\n\n## API Reference\n\n### Task.init(initClosure:)\n\nDefine your `task` inside `initClosure`.\n\n```swift\nlet task = Task\u003cFloat, NSString?, NSError\u003e { progress, fulfill, reject, configure in\n\n    player.doSomethingWithCompletion { (value: NSString?, error: NSError?) in\n        if error == nil {\n            fulfill(value)\n        }\n        else {\n            reject(error)\n        }\n    }\n}\n```\n\nIn order to pipeline future `task.value` or `task.errorInfo` (tuple of `(error: Error?, isCancelled: Bool)`) via `then()`/`success()`/`failure()`, you have to call `fulfill(value)` and/or `reject(error)` inside `initClosure`.\n\nOptionally, you can call `progress(progressValue)` multiple times before calling `fulfill`/`reject` to transfer `progressValue` outside of the `initClosure`, notifying it to `task` itself.\n\nTo add `pause`/`resume`/`cancel` functionality to your `task`, use `configure` to wrap up the original one.\n\n```swift\n// NOTE: use weak to let task NOT CAPTURE player via configure\nconfigure.pause = { [weak player] in\n    player?.pause()\n}\nconfigure.resume = { [weak player] in\n    player?.resume()\n}\nconfigure.cancel = { [weak player] in\n    player?.cancel()\n}\n```\n\n### task.progress(_ progressClosure:) -\u003e task\n\n```swift\ntask.progress { (oldProgress: Progress?, newProgress: Progress) in\n    println(newProgress)\n    return\n}.success { ... }\n```\n\n`task.progress(progressClosure)` will add `progressClosure` to observe old/new `progressValue` which is notified from inside previous `initClosure`. This method will return **same task**, so it is useful to chain with forthcoming `then`/`success`/`failure`.\n\n### task.then(_ thenClosure:) -\u003e newTask\n\n`task.then(thenClosure)` will return a new task where `thenClosure` will be invoked when `task` is either **fulfilled** or **rejected**.\n\nThis case is similar to JavaScript's `promise.then(onFulfilled, onRejected)`.\n\n`thenClosure` can be two types of closure form:\n\n1. `thenClosure: (Value?, ErrorInfo?) -\u003e Value2` (flow: *task =\u003e newTask*)\n\n  ```swift\n  // let task will be fulfilled with value \"Hello\"\n\n  task.then { (value: String?, errorInfo: ErrorInfo?) -\u003e String in\n      // nil-check to find out whether task is fulfilled or rejected\n      if errorInfo == nil {\n          return \"\\(value!) World\"\n      }\n      else {\n          return \"\\(value!) Error\"\n      }\n  }.success { (value: String) -\u003e Void in\n      println(\"\\(value)\")  // Hello World\n      return\"\n  }\n  ```\n\n2. `thenClosure: (Value?, ErrorInfo?) -\u003e Task` (flow: *task =\u003e task2 =\u003e newTask*)\n\n  ```swift\n  // let task will be fulfilled with value \"Hello\"\n\n  task.then { (value: String?, errorInfo: ErrorInfo?) -\u003e Task\u003cFloat, String, NSError\u003e in\n      if errorInfo == nil {\n          // let task2 will be fulfilled with value \"\\(value!) Swift\"\n          let task2 = ...\n          return task2\n      }\n      else {\n          return someOtherTask\n      }\n  }.success { (value: String) -\u003e Void in\n      println(\"\\(value)\")  // Hello Swift\n      return\"\n  }\n  ```\n\n### task.success(_ successClosure:) -\u003e newTask\n\nSimilar to `then()` method, `task.success(successClosure)` will return a new task, but this time, `successClosure` will be invoked when task is **only fulfilled**.\n\nThis case is similar to JavaScript's `promise.then(onFulfilled)`.\n\n```swift\n// let task will be fulfilled with value \"Hello\"\n\ntask.success { (value: String) -\u003e String in\n  return \"\\(value) World\"\n}.success { (value: String) -\u003e Void in\n  println(\"\\(value)\")  // Hello World\n  return\"\n}\n```\n\n### task.failure(_ failureClosure:) -\u003e newTask\n\nJust the opposite of `success()`, `task.failure(failureClosure)` will return a new task where `failureClosure` will be invoked when task is **only rejected/cancelled**.\n\nThis case is similar to JavaScript's `promise.then(undefined, onRejected)` or `promise.catch(onRejected)`.\n\n```swift\n// let task will be rejected with error \"Oh My God\"\n\ntask.success { (value: String) -\u003e Void in\n    println(\"\\(value)\") // never reaches here\n    return\n}.failure { (error: NSError?, isCancelled: Bool) -\u003e Void in\n    println(\"\\(error!)\")  // Oh My God\n    return\n}\n```\n\n### task.try(_ tryCount:) -\u003e newTask\n\nSee [Retry-able section](#retry-able).\n\n### Task.all(_ tasks:) -\u003e newTask\n\n`Task.all(tasks)` is a new task that performs all `tasks` simultaneously and will be:\n\n- fulfilled when **all tasks are fulfilled**\n- rejected when **any of the task is rejected**\n\n### Task.any(_ tasks:) -\u003e newTask\n\n`Task.any(tasks)` is an opposite of `Task.all(tasks)` which will be:\n\n- fulfilled when **any of the task is fulfilled**\n- rejected when **all tasks are rejected**\n\n### Task.some(_ tasks:) -\u003e newTask\n\n`Task.some(tasks)` is a new task that performs all `tasks` without internal rejection, and is fulfilled with given `tasks`'s fulfilled values. Note that this new task **will be fulfilled with empty value-array, even though all `tasks` are rejected.**\n\n\n## Related Articles\n\n- [SwiftTask（Promise拡張）を使う - Qiita](http://qiita.com/inamiy/items/0756339aee35849384c3) (Japanese, ver 1.0.0)\n\n\n## Licence\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FReactKit%2FSwiftTask","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FReactKit%2FSwiftTask","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FReactKit%2FSwiftTask/lists"}