And in Spot the 80s Lyric today, that post title is Naked Eyes providing the soundtrack for today’s post; we’re going to take a look at an interesting new library from Google named simply
In general, a promise represents the eventual result of an asynchronous task, respectively the error reason when the task fails. Similar concepts are also called futures (see also wiki article: Futures and promises)…
Promises is a modern framework that implements the aforementioned synchronization construct in Swift and Objective-C.
- Simple: The framework has intuitive APIs that are well documented making it painless to integrate into new or existing code.
- Interoperable: Supports both Objective-C and Swift. Promises that are created in Objective-C can be used in Swift and vice versa.
- Lightweight: Has minimum overhead that achieves similar performance to GCD and completion handlers.
- Flexible: Observer blocks can be dispatched on any thread or custom queue.
- Safe: All promises and observer blocks are captured by GCD which helps avoid potential retain cycles.
- Tested: The framework has 100% test coverage.
One of the biggest concerns for all frameworks is the overhead they add on top of the standard library (GCD in this case).
The data below was collected by running performance tests for Objective-C and Swift on an iPhone 6s iOS 11.2.1 for the popular frameworks: PromiseKit, BrightFutures, Hydra, RxSwift and plain GCD for comparison…
There’s a whole bunch of metrics collected there, this is the one we found most interesting:
So if you do have a need to mix Objective-C and Swift async code, promises looks like a pretty good option to consider!
However, these benchmarking comparisons aren’t going to mean very much in typical usage patterns, so you should probably go with whatever syntax feels most collegial, really.
Also, those benchmarks are probably not up to date with PromiseKit, which just updated:
PromiseKit 6 Released
With PromiseKit our then did multiple things, and we relied on Swift to infer the correct then from context. However with multiple line thens it would fail to do this, and instead of telling you that the situation was ambiguous it would invent some other error. Often the dreaded cannot convert T to AnyPromise. We have a troubleshooting guide to combat this but I believe in tools that just work, and when you spend 4 years waiting for Swift to fix the issue and Swift doesn’t fix the issue, what do you do? We chose to find a solution at the higher level.
So we split then into then, done and map.
- then is fed the previous promise value and requires you return a promise.
- done is fed the previous promise value and returns a Void promise (which is 80% of chain usage)
- map is fed the previous promise value and requires you return a non-promise, ie. a value.
At first I was nervous about this. But with some use on real projects I quickly realized that done alone was making PromiseKit use much more pleasant. Because Swift has no inference to do about the return for done you can write many line closures without any pain. then and map still require you to specify the return types for closures if they are multiple line, but often they are single line because you are chaining promises encapsulated in other functions.
The result is a happier compiler, a happier you and also, pleasantly (and somewhat surprisingly), clearer intent for your chains.
There’s another recent release in this space that’s mostly under the radar so far but looks like it merits a close look as well:
It is loosely based on both PromiseKit and Hydra, with a few key distinctions:
- It uses atomics internally instead of creating a separate DispatchQueue for each promise. This means it’s faster and uses fewer resources.
- It provides full support for cancellable promises. PromiseKit supports detection of “cancelled” errors but has no way to request cancellation of a promise. Hydra supports cancelling a promise, but it can’t actually stop any work being done by the promise unless the promise body itself polls for the cancellation status (so e.g. a promise wrapping a network task can’t reasonably cancel the network task). Tomorrowland improves on this by allowing the promise body to observe the cancelled state, and allows linking cancellation of a child promise to its parent.
- Its Obj-C support makes use of generics for improved type safety and better documentation.
- Like Hydra but unlike PromiseKit, it provides a way to suppress a registered callback (e.g. because you don’t care about the result anymore and don’t want stale data affecting your UI). This is distinct from promise cancellation.
- Tomorrowland promises are fully generic over the error type, whereas both PromiseKit and Hydra only support using Error as the error type. This may result in more typing to construct a promise but it allows for much more powerful error handling. Tomorrowland also has some affordances for working with promises that use Error as the error type.
- Tomorrowland is fully thread-safe. I have no reason to believe PromiseKit isn’t, but (at the time of this writing) there are parts of Hydra that are incorrectly implemented in a non-thread-safe manner.
and here’s another:
Getting Started with Deferred
At Big Nerd Ranch, we have created a library called Deferred, which makes it easier for developers to work with data that is not yet available but will be at some point in the future. For example, when you’re downloading data from the network or parsing data, the data is not yet available because some work needs to be completed first. You might know this concept as Futures or Promises, but if not, don’t worry—this post will help you gain a better understanding…
…It was originally inspired by OCaml’s Deferred library.
And there’s a couple more mentioned in this excellent article on the subject,
Under the hood of Futures & Promises in Swift
So there’s a veritable plethora of abstractions to choose from.
Long as we’re on the topic of asynchronicity and all, here’s a good read on parallelization:
Parallel programming with Swift: Basics and Parallel programming with Swift: Operations
and a couple task libraries to take a look at:
Queuer is a queue manager, built on top of OperationQueue and Dispatch (aka GCD).
It allows you to create any synchronous and asynchronous task easily, with just a few lines.
Here is the list of all the features:
- Works on all Swift compatible platforms (even Linux *)
- Easy to use
- Well documented (100% documented)
- Well tested (currently 99% code coverage)
- Create an operation block
- Create a single operation
- Create chained operations
- Manage a centralized queue
- Create unlimited queue
- Declare how many concurrent operation a queue can handle
- Create a network request operation *
- Create a network download operation *
- Create a network upload operation *
- Ability to restore uncompleted network operations *
A Swift framework inspired by WWDC 2015 Advanced NSOperations session. Previously known as Operations, developed by @danthorpe with a lot of help from our fantastic community…
And finally, here’s some lower level tidbits that are worth noting if you have challenges where you need to go right to the metal:
Friday Q&A 2017-10-27: Locks, Thread Safety, and Swift: 2017 Edition
Back in the dark ages of Swift 1, I wrote an article about locks and thread safety in Swift. The march of time has made it fairly obsolete, and reader Seth Willits suggested I update it for the modern age, so here it is!
Initializing on the main thread using dispatch_once without race conditions
We recently refactored the Khan Academy iOS app to use Core Data instead of the haphazard system of JSON files which we were previously using. In this post I’ll explain how to run initialization code that has to happen on the main thread without worrying about race conditions…
Leaky abstractions in Swift with DispatchQueue
At Q42 we have some apps with very occasional weird multi-threading issues. After a bunch of debugging Mathijs Kadijk and I figured out it had something to do with DispatchSpecificKey…
A Simple Approach to Thread-Safe Networking in iOS Apps
With our newfound understanding of URLSession‘s threading model, let’s sketch out how we might extend it to easily support running two requests in parallel. (The source code for this section is available on GitHub, ready for you to drop into a Playground.)…
Parallel programming with Swift: Promises and Basics and Operations and What Could Go Wrong?
Implementing Promises in Swift
Functional networking in Swift
Avoiding race conditions in Swift