{"id":16625449,"url":"https://github.com/frouo/promise-lite","last_synced_at":"2025-10-30T01:30:56.284Z","repository":{"id":42427564,"uuid":"259025798","full_name":"frouo/promise-lite","owner":"frouo","description":"Lets chain asynchronous methods. Pure Swift, 100% tested, lightweight with ~150 lines of code only","archived":false,"fork":false,"pushed_at":"2022-07-22T06:25:51.000Z","size":375,"stargazers_count":11,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-02T05:51:16.513Z","etag":null,"topics":["async","chaining","javascript","pod","promise","promises","swift"],"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/frouo.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2020-04-26T12:33:04.000Z","updated_at":"2022-04-06T16:59:37.000Z","dependencies_parsed_at":"2022-09-10T18:21:47.932Z","dependency_job_id":null,"html_url":"https://github.com/frouo/promise-lite","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frouo%2Fpromise-lite","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frouo%2Fpromise-lite/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frouo%2Fpromise-lite/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frouo%2Fpromise-lite/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/frouo","download_url":"https://codeload.github.com/frouo/promise-lite/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238920761,"owners_count":19552673,"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","chaining","javascript","pod","promise","promises","swift"],"created_at":"2024-10-12T04:05:42.645Z","updated_at":"2025-10-30T01:30:55.864Z","avatar_url":"https://github.com/frouo.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"![chaining](https://cocoricostudio.com/promiselite/readme.png)\n\n# PromiseLite\n\n**Lets chain async and synch functions**\n\n[![CI Status](https://travis-ci.com/frouo/promise-lite.svg?branch=master)](https://travis-ci.com/github/frouo/promise-lite)\n[![codecov](https://codecov.io/gh/frouo/promise-lite/branch/master/graph/badge.svg)](https://codecov.io/gh/frouo/promise-lite)\n[![Version](https://img.shields.io/cocoapods/v/PromiseLite.svg?style=flat)](https://cocoapods.org/pods/PromiseLite)\n[![License](https://img.shields.io/cocoapods/l/PromiseLite.svg?style=flat)](https://cocoapods.org/pods/PromiseLite)\n[![Platform](https://img.shields.io/cocoapods/p/PromiseLite.svg?style=flat)](https://cocoapods.org/pods/PromiseLite)\n\nPromiseLite is an implementation of [Javascript Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) concept, in Swift.\n\nIt is pure Swift, 100% tested, and very lightweight, ~150 lines of code.\n\n📜 [Complete Documentation](https://frouo.github.io/promise-lite/)\n\n## Installation\n\nPromiseLite is available through [CocoaPods](https://cocoapods.org). Add the following line to your Podfile:\n\n```ruby\npod 'PromiseLite'\n```\n\nTip: add the line `typealias Promise = PromiseLite` in your `AppDelegate.swift` (or elsewhere), it's shorter. For the rest of the page, I assume you did that.\n\n## Get started\n\nStart using promises in your code and chain async and sync functions within 5 minutes.\n\nLet's say you have the following function that uses completion block to deal with asynchronous operation. You might be familiar with:\n\n```swift\nfunc fetch(url: URL, completion: @escaping (Result\u003cData, Error\u003e) -\u003e Void) {\n  URLSession.shared.dataTask(with: url) { data, response, error in\n    if let data = data {\n      completion(.success(data))\n    } else {\n      completion(.failure(AppError.noData))\n    }\n  }\n}\n```\n\nTo be able to chain calls, you have to get rid of the completion block. Say hello to plromises!\n\nCreate another function that returns the promise of retrieving `Data`. In this new function, you can use the previously defined `fetch(url:completion:)` function within the `Promise`'s closure:\n\n```swift\nfunc fetch(url: URL) -\u003e Promise\u003cData\u003e {\n  Promise { resolve, reject in\n    fetch(url: url) { result in\n      switch result {\n      case .success(let data): resolve(data)\n      case .failure(let error): reject(error)\n      }\n    }\n  }\n}\n```\n\nNow you can use `flatMap` to chain promises together, and `map` to chain a promise to a regular function.\n\n```swift\nlet url = URL(string: \"https://your.endpoint.com/user/36\")!\n\nfetch(url: url)\n  .map     { try JSONDecoder().decode(User.self, from: $0) }\n  .map     { $0.age \u003e= 18 ? $0 : try { throw AppError.userIsMinor }() }\n  .flatMap { fetchContents(user: $0) }\n  .map     { display(contents: $0) }\n  .catch   { display(error: $0) }\n```\n\n**That's it!** 🎯\n\nJust for comparison, the above chaining is equivalent to the following code using completion blocks 🤯\n\n```swift\n// fetch(url: url) { result in\n//   switch result {\n//   case .success(let data):\n//     do {\n//       let user = try JSONDecoder().decode(User.self, from: data)\n//\n//       guard user.age \u003e= 18 else {\n//        display(error: AppError.userIsMinor)\n//        return\n//       }\n//\n//       fetchContents(user: user) { result2 in\n//         switch result2:\n//         case .success(let contents): display(contents: contents)\n//         case .failure(let error): display(error: error)\n//       }\n//     } catch {\n//       display(error: error)\n//     }\n//   case .failure(let error):\n//     display(error: error)\n//   }\n// }\n```\n\n## Promises\n\n### What is a promise?\n\nA promise represents the eventual result of an operation (async or sync).\n\nIts initialization parameter is called the `executor`. It is a closure that takes two functions as parameters:\n\n- `resolve`: a function that takes a value parameter (the result of the promise)\n- `reject`: a funtion that takes an error parameter\n\nFor exemple, we can define a promise this way:\n\n```swift\nfunc divide(a: Double, by b: Double) -\u003e Promise\u003cDouble\u003e {\n  let executor: ((Double) -\u003e Void, (Error) -\u003e Void) -\u003e Void = { resolve, reject in\n    b != 0\n      ? resolve(a / b)\n      : reject(AppError.cannotDivideByZero)\n  }\n  return Promise\u003cDouble\u003e(executor)\n}\n```\n\nFortunately Swift offers some syntax shorthand and is able to infer types. The preceding code can therefore be simplified as follows:\n\n```swift\nfunc divide(a: Double, by b: Double) -\u003e Promise\u003cDouble\u003e {\n  Promise { resolve, reject in\n    b != 0\n      ? resolve(a / b)\n      : reject(AppError.cannotDivideByZero)\n  }\n}\n```\n\n### More exemples...\n\nHere is an example of a sync function that takes an string parameter and returns the promise of an URL.\n\n```swift\nfunc url(from urlStr: String) -\u003e Promise\u003cURL\u003e {\n  Promise { resolve, reject in\n    if let url = URL(string: urlStr) {\n      resolve(url) // ✅ the url string is valid, call `resolve`\n    } else {\n      reject(AppError.invalidUrl) // ❌ the url string is not valid, call `reject`\n    }\n  }\n}\n```\n\nHere is a suggestion for wrapping `dataTask` into a promise that retrieves `Data`:\n\n```swift\nfunc fetch(url: URL) -\u003e Promise\u003cData\u003e {\n  Promise { resolve, reject in\n    URLSession.shared.dataTask(with: url) { data, response, error in\n      if let error = error {\n        reject(error) // ❌ an error occured, call `reject` or `throw`\n        return\n      }\n\n      guard let data = data else {\n        throw AppError.noData // ❌ could not retrieve data, call `reject` or `throw`\n        return\n      }\n\n      resolve(data) // ✅ data retrieved, call `resolve`\n    }\n  }\n}\n```\n\n### Helpers\n\n```swift\nPromise.resolve(\"foo\") // is equivalent to `Promise { resolve, _ in resolve(\"foo\") }`\n```\n\n```swift\nPromise\u003cString\u003e.reject(AppError.💥) // is equivalent to `Promise\u003cString\u003e { _, reject in reject(AppError.💥) }`\n\n// Note that, in this situation, you must specify the type `\u003cString\u003e` because there is nothing in the executor that can help Swift guess the type.\n```\n\n### Good to know\n\n- The executor function, ie. `{ resolve, reject in ... }` is executed right away by the initializer during the process of initializing the promise object.\n- the first `resolve`, `reject` or `throw` that is reached **wins** and any further calls will be **ignored**.\n\n## Chaining\n\nUse `map` and `flatMap` to chain promises.\n\n**Tip**: make functions as small as possible so you can compose easily. Example:\n\n```swift\nPromise.resolve(\"https://your.endpoint.com/user/\\(id)\")\n  .flatMap { url(from: $0) }\n  .flatMap { fetch(url: $0) }\n  .map     { try JSONDecoder().decode(User.self, from: $0) }\n  .map     { $0.age \u003e= 18 }\n  .flatMap { $0 ? fetchContents() : Promise.reject(AppError.userIsUnderage) }\n  .map     { display(contents: $0) }\n```\n\nIn the above example, we start with a string `https://your.endpoint.com/user/\\(id)`, then we call `url(from:)` to transform the `string` into an `URL`, etc...\n\n## Handling errors\n\nAn error does propagate until it is catched with `catch` or `flatCatch`. Once catched, the chaining is restored and continues.\n\n```swift\nPromise.resolve(\"not://a.va|id.url\")\n  .flatMap { url(from: $0) } // 💥 this promise rejects because the url is invalid\n  .flatMap { /* not reached */ }\n  .map     { /* not reached */ }\n  .map     { /* not reached */ }\n  .flatMap { /* not reached */ }\n  .map     { /* not reached */ }\n  .catch   { /* REACHED! */ }\n  .map     { /* REACHED! */ }\n  ...\n```\n\n## How to debug a chaining?\n\nWatch promise lifecycle by setting `PromiseLiteConfiguration.debugger` instance. This instance is called when a promise starts and when it resolves or rejects. `PromiseLite` provides a default implementation of the `PromiseLiteDebugger` protocol: `DefaultPromiseLiteDebugger(output:)`.\n\n```swift\n// Do the following to print default debugger output in the console.\nPromiseLiteConfiguration.debugger = DefaultPromiseLiteDebugger { print($0) }\n```\n\nIn addition, a promise can be initialized with a description so it is easier to understand which promise is currently being executed. By default, the description of a promise is `PromiseLite\u003cTheType\u003e`.\n\n```swift\nfunc fetchUser(id: String) -\u003e PromiseLite\u003cUser\u003e {\n  PromiseLite\u003cUser\u003e(\"fetch user\") { resolve, reject in\n    ...\n  }\n}\n\nfunc saveInDatabase(user: User) -\u003e PromiseLite\u003cBool\u003e {\n  PromiseLite\u003cBool\u003e(\"save in db\") { resolve, reject in\n    ...\n  }\n}\n\nfetchUser(id: \"123\")\n  .flatMap { saveInDatabase(user: $0) }\n  .map { [weak self] _ in self?.updateUI() }\n  .catch { [weak self] err in self?.updateUI(error: err) }\n\n// The above chaining will result in the following logs in the console:\n// 🔗 | fetch user resolves ✅ in 1.36 sec\n// 🔗 | save in db resolves ✅ in 0.72 sec\n// 🔗 | PromiseLite\u003c()\u003e resolves ✅ in 0.00 sec\n// 🔗 | PromiseLite\u003c()\u003e resolves ✅ in 0.00 sec\n// Note that `map` and `catch` implicitly creates a promise with the default description. Since `updateUI` is a function that returns void, the type's value of the implicity created promise is `()`.\n// Note that `catch` actually resolves because it implicitly creates a promise that resolves regardless of whether the previous promise resolved or rejected.\n\n// In case, `fetchUser(id:)` would reject, the above chaining would result in the following logs in the console:\n// 🔗 | fetch user rejects ❌ in 1.36 sec\n// 🔗 | save in db rejects ❌ in 0.00 sec\n// 🔗 | PromiseLite\u003c()\u003e rejects ❌ in 0.00 sec\n// 🔗 | PromiseLite\u003c()\u003e resolves ✅ in 0.00 sec\n// Note that rejection does propagate until `catch` handle the error returning a promise that resolves.\n```\n\n## Changelog\n\nVisit [CHANGELOG.md](https://github.com/frouo/promise-lite/blob/master/CHANGELOG.md)\n\n## Authors\n\n- François Rouault\n\nFeel free to submit merge request.\n\n## License\n\nPromiseLite 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%2Ffrouo%2Fpromise-lite","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffrouo%2Fpromise-lite","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrouo%2Fpromise-lite/lists"}