{"id":15132213,"url":"https://github.com/thomvis/brightfutures","last_synced_at":"2025-09-29T00:31:27.767Z","repository":{"id":712079,"uuid":"20614919","full_name":"Thomvis/BrightFutures","owner":"Thomvis","description":"Write great asynchronous code in Swift using futures and promises","archived":true,"fork":false,"pushed_at":"2022-07-02T18:27:43.000Z","size":960,"stargazers_count":1903,"open_issues_count":0,"forks_count":188,"subscribers_count":50,"default_branch":"master","last_synced_at":"2025-09-26T05:43:07.826Z","etag":null,"topics":["asynchronous-programming","concurrency","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/Thomvis.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":"2014-06-08T10:57:27.000Z","updated_at":"2025-09-07T19:22:38.000Z","dependencies_parsed_at":"2022-07-21T22:02:57.983Z","dependency_job_id":null,"html_url":"https://github.com/Thomvis/BrightFutures","commit_stats":null,"previous_names":[],"tags_count":50,"template":false,"template_full_name":null,"purl":"pkg:github/Thomvis/BrightFutures","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thomvis%2FBrightFutures","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thomvis%2FBrightFutures/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thomvis%2FBrightFutures/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thomvis%2FBrightFutures/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Thomvis","download_url":"https://codeload.github.com/Thomvis/BrightFutures/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thomvis%2FBrightFutures/sbom","scorecard":{"id":141102,"data":{"date":"2025-08-11","repo":{"name":"github.com/Thomvis/BrightFutures","commit":"a4b4cb27ef1525739988184dac73a1a462f9d62d"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.9,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"project is archived","details":["Warn: Repository is archived."],"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/tests.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":1,"reason":"Found 3/29 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/tests.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/Thomvis/BrightFutures/tests.yml/master?enable=pin","Info:   0 out of   1 GitHub-owned GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 4 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-16T08:11:05.642Z","repository_id":712079,"created_at":"2025-08-16T08:11:05.642Z","updated_at":"2025-08-16T08:11:05.642Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":277149964,"owners_count":25769492,"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","status":"online","status_checked_at":"2025-09-26T02:00:09.010Z","response_time":78,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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-programming","concurrency","futures","promises","swift"],"created_at":"2024-09-26T04:04:39.744Z","updated_at":"2025-09-29T00:31:27.251Z","avatar_url":"https://github.com/Thomvis.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"BrightFutures\n=============\n\n**:warning: BrightFutures has reached end-of-life.** After a long period of limited development activity, Swift's Async/Await has made the library obsolete. Please consider migrating from BrightFutures to async/await. When you do so, the async `get()` method will prove to be useful:\n\n```swift\n// in an async context...\n\nlet userFuture = User.logIn(username, password)\nlet user = try await userFuture.get()\n\n// or simply:\nlet posts = try await Posts.fetchPosts(user).get()\n```\n\n\n\nThe remainder of the README has not been updated recently, but is preserved for historic reasons.\n\n---\n\nHow do you leverage the power of Swift to write great asynchronous code? BrightFutures is our answer.\n\nBrightFutures implements proven [functional concepts](http://en.wikipedia.org/wiki/Futures_and_promises) in Swift to provide a powerful alternative to completion blocks and support typesafe error handling in asynchronous code.\n\nThe goal of BrightFutures is to be *the* idiomatic Swift implementation of futures and promises.\nOur Big Hairy Audacious Goal (BHAG) is to be copy-pasted into the Swift standard library.\n\nThe stability of BrightFutures has been proven through extensive use in production. It is currently being used in several apps, with a combined total of almost 500k monthly active users. If you use BrightFutures in production, we'd love to hear about it!\n\n## Latest news\n[![Join the chat at https://gitter.im/Thomvis/BrightFutures](https://badges.gitter.im/Thomvis/BrightFutures.svg)](https://gitter.im/Thomvis/BrightFutures?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge) [![GitHub Workflow tests.yml status badge](https://github.com/Thomvis/BrightFutures/actions/workflows/tests.yml/badge.svg?branch=master)](https://travis-ci.org/Thomvis/BrightFutures) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![CocoaPods version](https://img.shields.io/cocoapods/v/BrightFutures.svg)](https://cocoapods.org/pods/BrightFutures) [![CocoaPods](https://img.shields.io/cocoapods/metrics/doc-percent/BrightFutures.svg?maxAge=2592000)](http://cocoadocs.org/docsets/BrightFutures)\n\nBrightFutures 8.0 is now available! This update adds Swift 5 compatibility.\n\n## Installation\n### [CocoaPods](http://cocoapods.org/)\n\n1. Add the following to your [Podfile](http://guides.cocoapods.org/using/the-podfile.html):\n\n    ```rb\n    pod 'BrightFutures'\n    ```\n\n2. Integrate your dependencies using frameworks: add `use_frameworks!` to your Podfile. \n3. Run `pod install`.\n\n### [Carthage](https://github.com/Carthage/Carthage)\n\n1. Add the following to your [Cartfile](https://github.com/Carthage/Carthage/blob/master/Documentation/Artifacts.md#cartfile):\n\n    ```\n    github \"Thomvis/BrightFutures\"\n    ```\n\n2. Run `carthage update` and follow the steps as described in Carthage's [README](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application).\n\n## Documentation\n- This README covers almost all features of BrightFutures\n- The [tests](Tests/BrightFuturesTests) contain (trivial) usage examples for every feature (97% test coverage)\n- The primary author, Thomas Visser, gave [a talk](https://www.youtube.com/watch?v=lgJT2KMMEmU) at the April 2015 CocoaHeadsNL meetup\n- The [Highstreet Watch App](https://github.com/GetHighstreet/HighstreetWatchApp) was an Open Source WatchKit app that made extensive use of an earlier version of BrightFutures\n\n## Examples\nWe write a lot of asynchronous code. Whether we're waiting for something to come in from the network or want to perform an expensive calculation off the main thread and then update the UI, we often do the 'fire and callback' dance. Here's a typical snippet of asynchronous code:\n\n```swift\nUser.logIn(username, password) { user, error in\n    if !error {\n        Posts.fetchPosts(user, success: { posts in\n            // do something with the user's posts\n        }, failure: handleError)\n    } else {\n        handleError(error) // handeError is a custom function to handle errors\n    }\n}\n```\n\nNow let's see what BrightFutures can do for you:\n\n```swift\nUser.logIn(username, password).flatMap { user in\n    Posts.fetchPosts(user)\n}.onSuccess { posts in\n    // do something with the user's posts\n}.onFailure { error in\n    // either logging in or fetching posts failed\n}\n```\n\nBoth `User.logIn` and `Posts.fetchPosts` now immediately return a `Future`. A future can either fail with an error or succeed with a value, which can be anything from an Int to your custom struct, class or tuple. You can keep a future around and register for callbacks for when the future succeeds or fails at your convenience.\n\nWhen the future returned from `User.logIn` fails, e.g. the username and password did not match, `flatMap` and `onSuccess` are skipped and `onFailure` is called with the error that occurred while logging in. If the login attempt succeeded, the resulting user object is passed to `flatMap`, which 'turns' the user into an array of his or her posts. If the posts could not be fetched, `onSuccess` is skipped and `onFailure` is called with the error that occurred when fetching the posts. If the posts could be fetched successfully, `onSuccess` is called with the user's posts.\n\nThis is just the tip of the proverbial iceberg. A lot more examples and techniques can be found in this readme, by browsing through the tests or by checking out the official companion framework [FutureProofing](https://github.com/Thomvis/FutureProofing).\n\n## Wrapping expressions\nIf you already have a function (or really any expression) that you just want to execute asynchronously and have a Future to represent its result, you can easily wrap it in an `asyncValue` block:\n\n```swift\nDispatchQueue.global().asyncValue {\n    fibonacci(50)\n}.onSuccess { num in\n    // value is 12586269025\n}\n```\n\n`asyncValue` is defined in an extension on GCD's `DispatchQueue`. While this is really short and simple, it is equally limited. In many cases, you will need a way to indicate that the task failed. To do this, instead of returning the value, you can return a Result. Results can indicate either a success or a failure:\n\n```swift\nenum ReadmeError: Error {\n    case RequestFailed, TimeServiceError\n}\n\nlet f = DispatchQueue.global().asyncResult { () -\u003e Result\u003cDate, ReadmeError\u003e in\n    if let now = serverTime() {\n        return .success(now)\n    }\n    \n    return .failure(ReadmeError.TimeServiceError)\n}\n\nf.onSuccess { value in\n    // value will the NSDate from the server\n}\n```\n\nThe future block needs an explicit type because the Swift compiler is not able to deduce the type of multi-statement blocks.\n\nInstead of wrapping existing expressions, it is often a better idea to use a Future as the return type of a method so all call sites can benefit. This is explained in the next section.\n\n## Providing Futures\nNow let's assume the role of an API author who wants to use BrightFutures. A Future is designed to be read-only, except for the site where the Future is created. This is achieved via an initialiser on Future that takes a closure, the completion scope, in which you can complete the Future. The completion scope has one parameter that is also a closure which is invoked to set the value (or error) in the Future.\n\n```swift\nfunc asyncCalculation() -\u003e Future\u003cString, Never\u003e {\n    return Future { complete in\n        DispatchQueue.global().async {\n            // do a complicated task and then hand the result to the promise:\n            complete(.success(\"forty-two\"))\n        }\n    }\n}\n```\n\n`Never` indicates that the `Future` cannot fail. This is guaranteed by the type system, since `Never` has no initializers. As an alternative to the completion scope, you could also create a `Promise`, which is the writeable equivalent of a Future, and store it somewhere for later use.\n\n## Callbacks\nYou can be informed of the result of a `Future` by registering callbacks: `onComplete`, `onSuccess` and `onFailure`. The order in which the callbacks are executed upon completion of the future is not guaranteed, but it is guaranteed that the callbacks are executed serially. It is not safe to add a new callback from within a callback of the same future.\n\n## Chaining callbacks\n\nUsing the `andThen` function on a `Future`, the order of callbacks can be explicitly defined. The closure passed to `andThen` is meant to perform side-effects and does not influence the result. `andThen` returns a new Future with the same result as this future that completes after the closure has been executed.\n\n```swift\nvar answer = 10\n    \nlet _ = Future\u003cInt, Never\u003e(value: 4).andThen { result in\n    switch result {\n    case .success(let val):\n        answer *= val\n    case .failure(_):\n        break\n    }\n}.andThen { result in\n    if case .success(_) = result {\n        answer += 2\n    }\n}\n\n// answer will be 42 (not 48)\n```\n\n## Functional Composition\n\n### map\n\n`map` returns a new Future that contains the error from this Future if this Future failed, or the return value from the given closure that was applied to the value of this Future.\n\n```swift\nfibonacciFuture(10).map { number -\u003e String in\n    if number \u003e 5 {\n        return \"large\"\n    }\n    return \"small\"\n}.map { sizeString in\n    sizeString == \"large\"\n}.onSuccess { numberIsLarge in\n    // numberIsLarge is true\n}\n```\n\n### flatMap\n\n`flatMap` is used to map the result of a future to the value of a new Future.\n\n```swift\nfibonacciFuture(10).flatMap { number in\n    fibonacciFuture(number)\n}.onSuccess { largeNumber in\n    // largeNumber is 139583862445\n}\n```\n\n### zip\n\n```swift\nlet f = Future\u003cInt, Never\u003e(value: 1)\nlet f1 = Future\u003cInt, Never\u003e(value: 2)\n\nf.zip(f1).onSuccess { a, b in\n    // a is 1, b is 2\n}\n```\n\n### filter\n```swift\nFuture\u003cInt, Never\u003e(value: 3)\n    .filter { $0 \u003e 5 }\n    .onComplete { result in\n        // failed with error NoSuchElementError\n    }\n\nFuture\u003cString, Never\u003e(value: \"Swift\")\n    .filter { $0.hasPrefix(\"Sw\") }\n    .onComplete { result in\n        // succeeded with value \"Swift\"\n    }\n```\n\n## Recovering from errors\nIf a `Future` fails, use `recover` to offer a default or alternative value and continue the callback chain.\n\n```swift\n// imagine a request failed\nFuture\u003cInt, ReadmeError\u003e(error: .RequestFailed)\n    .recover { _ in // provide an offline default\n        return 5\n    }.onSuccess { value in\n        // value is 5 if the request failed or 10 if the request succeeded\n    }\n```\n\nIn addition to `recover`, `recoverWith` can be used to provide a Future that will provide the value to recover with.\n\n## Utility Functions\nBrightFutures also comes with a number of utility functions that simplify working with multiple futures. These are implemented as free (i.e. global) functions to work around current limitations of Swift.\n\n## Fold\nThe built-in `fold` function allows you to turn a list of values into a single value by performing an operation on every element in the list that *consumes* it as it is added to the resulting value. A trivial usecase for fold would be to calculate the sum of a list of integers.\n\nFolding a list of Futures is not very convenient with the built-in `fold` function, which is why BrightFutures provides one that works especially well for our use case. BrightFutures' `fold` turns a list of Futures into a single Future that contains the resulting value. This allows us to, for example, calculate the sum of the first 10 Future-wrapped elements of the fibonacci sequence:\n\n```swift\nlet fibonacciSequence = [fibonacciFuture(1), fibonacciFuture(2),  ..., fibonacciFuture(10)]\n\n// 1+1+2+3+5+8+13+21+34+55\nfibonacciSequence.fold(0, f: { $0 + $1 }).onSuccess { sum in\n    // sum is 143\n}\n```\n\n## Sequence\nWith `sequence`, you can turn a list of Futures into a single Future that contains a list of the results from those futures.\n\n```swift\nlet fibonacciSequence = [fibonacciFuture(1), fibonacciFuture(2),  ..., fibonacciFuture(10)]\n    \nfibonacciSequence.sequence().onSuccess { fibNumbers in\n    // fibNumbers is an array of Ints: [1, 1, 2, 3, etc.]\n}\n```\n\n## Traverse\n`traverse` combines `map` and `fold` in one convenient function. `traverse` takes a list of values and a closure that takes a single value from that list and turns it into a Future. The result of `traverse` is a single Future containing an array of the values from the Futures returned by the given closure.\n\n```swift\n(1...10).traverse {\n    i in fibonacciFuture(i)\n}.onSuccess { fibNumbers in\n    // fibNumbers is an array of Ints: [1, 1, 2, 3, etc.]\n}\n```\n\n## Delay\n`delay` returns a new Future that will complete after waiting for the given interval with the result of the previous Future.\nTo simplify working with `DispatchTime` and `DispatchTimeInterval`, we recommend to use this [extension](https://gist.github.com/Thomvis/b378f926b6e1a48973f694419ed73aca).\n\n```swift\nFuture\u003cInt, Never\u003e(value: 3).delay(2.seconds).andThen { result in\n    // execute after two additional seconds\n}\n```\n\n## Default Threading Model\nBrightFutures tries its best to provide a simple and sensible default threading model. In theory, all threads are created equally and BrightFutures shouldn't care about which thread it is on. In practice however, the main thread is _more equal than others_, because it has a special place in our hearts and because you'll often want to be on it to do UI updates.\n\nA lot of the methods on `Future` accept an optional _execution context_ and a block, e.g. `onSuccess`, `map`, `recover` and many more. The block is executed (when the future is completed) in the given execution context, which in practice is a GCD queue. When the context is not explicitly provided, the following rules will be followed to determine the execution context that is used:\n\n- if the method is called from the main thread, the block is executed on the main queue\n- if the method is not called from the main thread, the block is executed on a global queue\n\nIf you want to have custom threading behavior, skip do do not the section. next [:wink:](https://twitter.com/nedbat/status/194452404794691584)\n\n## Custom execution contexts\nThe default threading behavior can be overridden by providing explicit execution contexts. You can choose from any of the built-in contexts or easily create your own. Default contexts include: any dispatch queue, any `NSOperationQueue` and the `ImmediateExecutionContext` for when you don't want to switch threads/queues.\n\n```swift\nlet f = Future\u003cInt, Never\u003e { complete in\n    DispatchQueue.global().async {\n        complete(.success(fibonacci(10)))\n    }\n}\n\nf.onComplete(DispatchQueue.main.context) { value in\n    // update the UI, we're on the main thread\n}\n```\n\nEven though the future is completed from the global queue, the completion closure will be called on the main queue.\n\n## Invalidation tokens\nAn invalidation token can be used to invalidate a callback, preventing it from being executed upon completion of the future. This is particularly useful in cases where the context in which a callback is executed changes often and quickly, e.g. in reusable views such as table views and collection view cells. An example of the latter:\n\n```swift\nclass MyCell : UICollectionViewCell {\n    var token = InvalidationToken()\n    \n    public override func prepareForReuse() {\n        super.prepareForReuse()\n        token.invalidate()\n        token = InvalidationToken()\n    }\n    \n    public func setModel(model: Model) {\n        ImageLoader.loadImage(model.image).onSuccess(token.validContext) { [weak self] UIImage in\n            self?.imageView.image = UIImage\n        }\n    }\n}\n```\n\nBy invalidating the token on every reuse, we prevent that the image of the previous model is set after the next model has been set.\n\nInvalidation tokens _do not_ cancel the task that the future represents. That is a different problem. With invalidation tokens, the result is merely ignored. Invalidating a token after the original future completed does nothing.\n\nIf you are looking for a way to cancel a running task, you could look into using [NSProgress](https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSProgress_Class/Reference/Reference.html).\n\n## Credits\n\nBrightFutures' primary author is [Thomas Visser](https://twitter.com/thomvis). He is lead iOS Engineer at [Highstreet](http://www.highstreetapp.com/). We welcome any feedback and pull requests. Get your name on [this list](https://github.com/Thomvis/BrightFutures/graphs/contributors)!\n\nBrightFutures was inspired by Facebook's [BFTasks](https://github.com/BoltsFramework/Bolts-iOS), the Promises \u0026 Futures implementation in [Scala](http://docs.scala-lang.org/overviews/core/futures.html) and Max Howell's [PromiseKit](https://github.com/mxcl/PromiseKit).\n\n## License\n\nBrightFutures 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%2Fthomvis%2Fbrightfutures","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthomvis%2Fbrightfutures","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthomvis%2Fbrightfutures/lists"}