{"id":1114,"url":"https://github.com/DimaMishchenko/EasyFutures","last_synced_at":"2025-07-30T20:32:35.194Z","repository":{"id":56909738,"uuid":"139244112","full_name":"DimaMishchenko/EasyFutures","owner":"DimaMishchenko","description":"Easy Swift Futures \u0026 Promises.","archived":true,"fork":false,"pushed_at":"2020-12-27T13:21:09.000Z","size":71,"stargazers_count":42,"open_issues_count":2,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-11-29T13:09:45.944Z","etag":null,"topics":["futures","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/DimaMishchenko.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":"2018-06-30T11:31:03.000Z","updated_at":"2024-10-20T19:20:32.000Z","dependencies_parsed_at":"2022-08-20T19:40:05.214Z","dependency_job_id":null,"html_url":"https://github.com/DimaMishchenko/EasyFutures","commit_stats":null,"previous_names":["dimamishchenko/easyfuture"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DimaMishchenko%2FEasyFutures","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DimaMishchenko%2FEasyFutures/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DimaMishchenko%2FEasyFutures/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DimaMishchenko%2FEasyFutures/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DimaMishchenko","download_url":"https://codeload.github.com/DimaMishchenko/EasyFutures/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228187535,"owners_count":17882322,"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":["futures","promises","swift"],"created_at":"2024-01-05T20:15:39.209Z","updated_at":"2024-12-04T20:31:01.180Z","avatar_url":"https://github.com/DimaMishchenko.png","language":"Swift","funding_links":[],"categories":["EventBus","Libs","Events [🔝](#readme)"],"sub_categories":["Getting Started","Events","Linter"],"readme":"# ❗️Archived now❗️\nSince Apple released [`Combine`](https://developer.apple.com/documentation/combine) framework, I decide to archive this repo.\nYou still can use this repo as an example of `Future`/`Promise` implementation in `Swift`.\n\n# EasyFutures\n[![Swift 4.2](https://img.shields.io/badge/Swift-4.2-orange.svg?style=flat)](https://developer.apple.com/swift/)\n[![CocoaPods compatible](https://img.shields.io/cocoapods/v/EasyFutures.svg)](https://cocoapods.org/pods/EasyFutures)\n[![Build Status](https://travis-ci.org/DimaMishchenko/EasyFutures.svg?branch=master)](https://travis-ci.org/DimaMishchenko/EasyFutures)\n[![codecov.io](https://codecov.io/gh/DimaMishchenko/EasyFutures/branch/master/graphs/badge.svg)](https://codecov.io/gh/DimaMishchenko/EasyFutures)\n[![Packagist](https://img.shields.io/packagist/l/doctrine/orm.svg)](LICENSE)\n\nSwift implementation of Futures \u0026 Promises. You can read more about Futures \u0026 Promises in Wikipedia: https://en.wikipedia.org/wiki/Futures_and_promises.\n\n**EasyFutures** is:\n- 100% Swift, [100% test coverage](https://codecov.io/gh/DimaMishchenko/EasyFutures).\n- easy to understand.\n- type safe (uses Swift generics).\n- the avoidance of a \"callback hell\".\n- out of the box errors handling (you don't need to use `do/catch`).\n- composeable ([`map`](#map), [`flatMap`](#flatmap), [`filter`](#filter), [`recover`](#recover), [`zip`](#zip), [`andThen`](#andthen), [`flatten`](#flatten)).\n- support sequences ([`fold`](#fold), [`traverse`](#traverse), [`sequence`](#sequence)).\n- [fully documented](#documentation).\n\n## Documentation\n- Full documentation and more examples you can find in [Playground](EasyFuturesPlayground.playground) (to use playground you should open it in `EasyFutures.xcodeproj` and build EasyFutures framework).\n- [Wiki](https://github.com/DimaMishchenko/EasyFutures/wiki) (full documentation and all examples).\n- [Unit tests](EasyFuturesTests).\n- [Examples section](#examples).\n\n## Requirements\n- iOS 9.0+\n\n## Installation\n### [CocoaPods](http://www.cocoapods.org):\n- Add the following line to your [`Podfile`](http://guides.cocoapods.org/using/the-podfile.html):\n``` ruby\npod 'EasyFutures'\n\n#for swift less than 4.2 use:\npod 'EasyFutures', '~\u003e 1.1.0'\n```\n- Add `use_frameworks!` to your [`Podfile`](http://guides.cocoapods.org/using/the-podfile.html).\n- Run `pod install`.\n- Add to files:\n``` swift\nimport EasyFutures\n```\n\n## Examples\nTraditional way to write asynchronous code:\n``` swift\nfunc loadChatRoom(_ completion: (_ chat: Chat?, _ error: Error?) -\u003e Void) {}\nfunc loadUser(id: String, _ completion: (_ user: User?, _ error: Error?) -\u003e Void) {}\n\nloadChatRoom { chat, error in\n    if let chat = chat {\n        loadUser(id: chat.ownerId) { user, error in\n            if let user = user {\n                print(user)\n                // owner loaded\n            } else {\n                // handle error\n            }\n        }\n    } else {\n        // handle error\n    }\n}\n```\nSame logic but with **EasyFutures**:\n``` swift\nfunc loadChatRoom() -\u003e Future\u003cChat\u003e\nfunc loadUser(id: String) -\u003e Future\u003cUser\u003e \n\nloadChatRoom().flatMap({ chat -\u003e Future\u003cUser\u003e in\n    // loading user\n    return loadUser(id: chat.ownerId)\n}).onSuccess { user in\n    // user loaded\n}.onError { error in\n    // handle error\n}\n```\n### Future\nFuture is an object that contains or will contain `result` which can be value or error. Usually result gets from some asynchronous process. To receive result you can define `onComplete`, `onSuccess`, `onError` callbacks.  \n``` swift\n\nfunc loadData() -\u003e Future\u003cString\u003e \n\nlet future = loadData()\n\nfuture.onComplete { result in\n    switch result {\n    case .value(let value):\n        // value\n    case .error(let error):\n        // error\n    }\n}\n\nfuture.onSuccess { data in\n    // value\n}.onError { error in\n    // error\n}\n```\n### Promise\nThe Promises are used to write functions that returns the Futures. The Promise contains the Future instance and can complete it.\n``` swift\nfunc loadData() -\u003e Future\u003cString\u003e {\n\n    // create the promise with String type\n    let promise = Promise\u003cString\u003e()\n\n    // check is url valid\n    guard let url = URL(string: \"https://api.github.com/emojis\") else {\n        // handle error\n        promise.error(ExampleError.invalidUrl)\n        return promise.future\n    }\n\n    // loading data from url\n    let task = URLSession.shared.dataTask(with: url) { (data, response, error) in\n        if let data = data, let string = String(data: data, encoding: .utf8) {\n            // return result\n            promise.success(string)\n        } else {\n            // handle error\n            promise.error(ExampleError.cantLoadData)\n        }\n    }\n    task.resume()\n\n    // return the future\n    return promise.future\n}\n\nloadData().onSuccess { data in\n    DispatchQueue.main.async {\n        self.label.text = data\n    }\n}.onError { error in\n    print(error)\n    // handle error\n}\n```\n## Composition\n### map\nReturns the new Future with the result you return to closure or with error if the first Future contains error.\n``` swift\n\nlet future = Future\u003cInt\u003e(value: 100)\nfuture.onSuccess { value in\n    // value == 100\n}\n\nlet mapFuture = future.map { value -\u003e String in\n    return \"\\(value) now it's string\"\n}\nmapFuture.onComplete { result in\n    switch result {\n    case .value(let value):\n        print(value) // \"100 now it's string\"\"\n    case .error(let error):\n        // handle error\n    }\n}\n```\n### flatMap\nReturns the new Future with the Future you return to closure or with error if the first Future contains error.\n``` swift\nlet future = Future\u003cInt\u003e(value: 1)\n\nlet flatMapFuture = future.flatMap { value -\u003e Future\u003cString\u003e in\n    return Future\u003cString\u003e(value: \"\\(value * 100)%\")\n}\nflatMapFuture.onSuccess { value in\n    print(value) // \"100%\"\n}\n```\n### filter\nReturns the Future if value satisfies the filtering else returns error.\n``` swift \nlet future = Future\u003cInt\u003e(value: 500)\n\nfuture.filter { value -\u003e Bool in\n    return value \u003e 100\n}.onComplete { result in\n    switch result {\n    case .value(let value):\n        print(value) // 100\n    case .error(let error):\n        print(error) // no error\n    }\n}\n\nfuture.filter { value -\u003e Bool in\n    return value \u003e 1000\n}.onComplete { result in\n    switch result {\n    case .value(let value):\n        print(value) // no value\n    case .error(let error):\n        print(error) // FutureError.filterError\n    }\n}\n```\n### recover\nIf the Future contains or will contain error you can recover it with the new value.\n``` swift \nlet future = Future\u003cInt\u003e(error: someError)\n\nfuture.recover { error -\u003e Int in\n    return 100\n}.onComplete { result in\n    switch result {\n    case .value(let value):\n        print(value) // 100\n    case .error(let error):\n        print(error) // no error\n    }\n}\n```\n### zip\nCombines two values into a tuple.\n``` swift \nlet first = Future\u003cInt\u003e(value: 1)\nlet second = Future\u003cInt\u003e(value: 2)\n\nfirst.zip(second).onSuccess { firstValue, secondValue in\n    print(firstValue) // 1\n    print(secondValue) // 2\n}\n```\n### andThen\nReturns the new Future with the same value.\n``` swift \nlet future = Future\u003cString\u003e(value: \"and\")\n\nfuture.andThen { value in\n    print(value) // \"and\"\n}.andThen { value in\n    print(value.count) // 3\n}\n```\n### flatten\nIf the value of the Future is the another Future you can flatten it.\n``` swift \nlet future = Future\u003cFuture\u003cString\u003e\u003e(value: Future\u003cString\u003e(value: \"value\"))\n\nfuture.onSuccess { value in\n    print(value) // Future\u003cString\u003e(value: \"value\")\n}\n\nfuture.flatten().onSuccess { value in\n    print(value) // \"value\"\n}\n```\n## Errors handling\n[`map`](#map), [`flatMap`](#flatmap), [`filter`](#filter) and [`recover`](#recover) can catch errors and return the Future with this error, so you don't need to handle it with `do/catch`.\n``` swift\nlet future = Future\u003cString\u003e(value: \"\")\nlet errorToThrow = NSError(domain: \"\", code: -1, userInfo: nil)\n\nfuture.map { value -\u003e String in\n    throw errorToThrow\n}.flatMap { value -\u003e Future\u003cString\u003e in\n    throw errorToThrow\n}.filter { value -\u003e Bool in\n    throw errorToThrow\n}.recover { error -\u003e String in\n    throw errorToThrow\n}\n```\n## Sequences\n**EasyFutures** provides some functions to help you work with the sequences of Futures.\n### fold\nYou can convert a list of the values into a single value. Fold returns the Future with this value. Fold takes default value and then you perform action with default value and every value from the list. Can catch errors.\n``` swift\nlet futures = [Future\u003cInt\u003e(value: 1), Future\u003cInt\u003e(value: 2), Future\u003cInt\u003e(value: 3)]\n\nfutures.fold(0) { defaultValue, currentValue -\u003e Int in\n    return defaultValue + currentValue\n}.onSuccess { value in\n    print(value) // 6\n}\n```\n### traverse\nTraverse can work with any sequence. Takes closure where you transform the value into the Future. Returns the Future which contains array of the values from the Futures returned by the closure.\n``` swift \n[1, 2, 3].traverse { number -\u003e Future\u003cString\u003e in\n    return Future\u003cString\u003e(value: \"\\(number * 100)\")\n}.onSuccess { value in\n    print(value) // [\"100\", \"200\", \"300\"]\n}\n```\n### sequence\nTransforms a list of the Futures into the single Future with an array of values.\n``` swift \nlet futures = [Future\u003cInt\u003e(value: 1), Future\u003cInt\u003e(value: 2), Future\u003cInt\u003e(value: 3)] // [Future\u003cInt\u003e, Future\u003cInt\u003e, Future\u003cInt\u003e]\nlet sequence = futures.sequence() // Future\u003c[Int]\u003e\nsequence.onSuccess { numbers in\n    print(numbers) // [1, 2, 3]\n}\n```\n## License\n**EasyFutures** is under MIT license. See the [LICENSE](LICENSE) file for more info.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDimaMishchenko%2FEasyFutures","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FDimaMishchenko%2FEasyFutures","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDimaMishchenko%2FEasyFutures/lists"}