Easy Swift Futures & Promises.

Overview

❗️ Archived now ❗️

Since Apple released Combine framework, I decide to archive this repo. You still can use this repo as an example of Future/Promise implementation in Swift.

EasyFutures

Swift 4.2 CocoaPods compatible Build Status codecov.io Packagist

Swift implementation of Futures & Promises. You can read more about Futures & Promises in Wikipedia: https://en.wikipedia.org/wiki/Futures_and_promises.

EasyFutures is:

Documentation

  • Full documentation and more examples you can find in Playground (to use playground you should open it in EasyFutures.xcodeproj and build EasyFutures framework).
  • Wiki (full documentation and all examples).
  • Unit tests.
  • Examples section.

Requirements

  • iOS 9.0+

Installation

CocoaPods:

  • Add the following line to your Podfile:
pod 'EasyFutures'

#for swift less than 4.2 use:
pod 'EasyFutures', '~> 1.1.0'
  • Add use_frameworks! to your Podfile.
  • Run pod install.
  • Add to files:
import EasyFutures

Examples

Traditional way to write asynchronous code:

func loadChatRoom(_ completion: (_ chat: Chat?, _ error: Error?) -> Void) {}
func loadUser(id: String, _ completion: (_ user: User?, _ error: Error?) -> Void) {}

loadChatRoom { chat, error in
    if let chat = chat {
        loadUser(id: chat.ownerId) { user, error in
            if let user = user {
                print(user)
                // owner loaded
            } else {
                // handle error
            }
        }
    } else {
        // handle error
    }
}

Same logic but with EasyFutures:

func loadChatRoom() -> Future<Chat>
func loadUser(id: String) -> Future<User> 

loadChatRoom().flatMap({ chat -> Future<User> in
    // loading user
    return loadUser(id: chat.ownerId)
}).onSuccess { user in
    // user loaded
}.onError { error in
    // handle error
}

Future

Future 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.

func loadData() -> Future<String> 

let future = loadData()

future.onComplete { result in
    switch result {
    case .value(let value):
        // value
    case .error(let error):
        // error
    }
}

future.onSuccess { data in
    // value
}.onError { error in
    // error
}

Promise

The Promises are used to write functions that returns the Futures. The Promise contains the Future instance and can complete it.

func loadData() -> Future<String> {

    // create the promise with String type
    let promise = Promise<String>()

    // check is url valid
    guard let url = URL(string: "https://api.github.com/emojis") else {
        // handle error
        promise.error(ExampleError.invalidUrl)
        return promise.future
    }

    // loading data from url
    let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
        if let data = data, let string = String(data: data, encoding: .utf8) {
            // return result
            promise.success(string)
        } else {
            // handle error
            promise.error(ExampleError.cantLoadData)
        }
    }
    task.resume()

    // return the future
    return promise.future
}

loadData().onSuccess { data in
    DispatchQueue.main.async {
        self.label.text = data
    }
}.onError { error in
    print(error)
    // handle error
}

Composition

map

Returns the new Future with the result you return to closure or with error if the first Future contains error.

let future = Future<Int>(value: 100)
future.onSuccess { value in
    // value == 100
}

let mapFuture = future.map { value -> String in
    return "\(value) now it's string"
}
mapFuture.onComplete { result in
    switch result {
    case .value(let value):
        print(value) // "100 now it's string""
    case .error(let error):
        // handle error
    }
}

flatMap

Returns the new Future with the Future you return to closure or with error if the first Future contains error.

let future = Future<Int>(value: 1)

let flatMapFuture = future.flatMap { value -> Future<String> in
    return Future<String>(value: "\(value * 100)%")
}
flatMapFuture.onSuccess { value in
    print(value) // "100%"
}

filter

Returns the Future if value satisfies the filtering else returns error.

let future = Future<Int>(value: 500)

future.filter { value -> Bool in
    return value > 100
}.onComplete { result in
    switch result {
    case .value(let value):
        print(value) // 100
    case .error(let error):
        print(error) // no error
    }
}

future.filter { value -> Bool in
    return value > 1000
}.onComplete { result in
    switch result {
    case .value(let value):
        print(value) // no value
    case .error(let error):
        print(error) // FutureError.filterError
    }
}

recover

If the Future contains or will contain error you can recover it with the new value.

let future = Future<Int>(error: someError)

future.recover { error -> Int in
    return 100
}.onComplete { result in
    switch result {
    case .value(let value):
        print(value) // 100
    case .error(let error):
        print(error) // no error
    }
}

zip

Combines two values into a tuple.

let first = Future<Int>(value: 1)
let second = Future<Int>(value: 2)

first.zip(second).onSuccess { firstValue, secondValue in
    print(firstValue) // 1
    print(secondValue) // 2
}

andThen

Returns the new Future with the same value.

let future = Future<String>(value: "and")

future.andThen { value in
    print(value) // "and"
}.andThen { value in
    print(value.count) // 3
}

flatten

If the value of the Future is the another Future you can flatten it.

let future = Future<Future<String>>(value: Future<String>(value: "value"))

future.onSuccess { value in
    print(value) // Future<String>(value: "value")
}

future.flatten().onSuccess { value in
    print(value) // "value"
}

Errors handling

map, flatMap, filter and recover can catch errors and return the Future with this error, so you don't need to handle it with do/catch.

let future = Future<String>(value: "")
let errorToThrow = NSError(domain: "", code: -1, userInfo: nil)

future.map { value -> String in
    throw errorToThrow
}.flatMap { value -> Future<String> in
    throw errorToThrow
}.filter { value -> Bool in
    throw errorToThrow
}.recover { error -> String in
    throw errorToThrow
}

Sequences

EasyFutures provides some functions to help you work with the sequences of Futures.

fold

You 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.

let futures = [Future<Int>(value: 1), Future<Int>(value: 2), Future<Int>(value: 3)]

futures.fold(0) { defaultValue, currentValue -> Int in
    return defaultValue + currentValue
}.onSuccess { value in
    print(value) // 6
}

traverse

Traverse 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.

[1, 2, 3].traverse { number -> Future<String> in
    return Future<String>(value: "\(number * 100)")
}.onSuccess { value in
    print(value) // ["100", "200", "300"]
}

sequence

Transforms a list of the Futures into the single Future with an array of values.

let futures = [Future<Int>(value: 1), Future<Int>(value: 2), Future<Int>(value: 3)] // [Future<Int>, Future<Int>, Future<Int>]
let sequence = futures.sequence() // Future<[Int]>
sequence.onSuccess { numbers in
    print(numbers) // [1, 2, 3]
}

License

EasyFutures is under MIT license. See the LICENSE file for more info.

You might also like...
Modern thread-safe and type-safe key-value observing for Swift and Objective-C

Now Archived and Forked PMKVObserver will not be maintained in this repository going forward. Please use, create issues on, and make PRs to the fork o

A library for reactive and unidirectional Swift applications
A library for reactive and unidirectional Swift applications

ReactorKit is a framework for a reactive and unidirectional Swift application architecture. This repository introduces the basic concept of ReactorKit

ReSwift is a Redux-like implementation of the unidirectional data flow architecture in Swift.
ReSwift is a Redux-like implementation of the unidirectional data flow architecture in Swift.

ReSwift is a Redux-like implementation of the unidirectional data flow architecture in Swift. ReSwift helps you to separate three important concerns of your app's components.

🐌 snail - An observables framework for Swift
🐌 snail - An observables framework for Swift

🐌 snail A lightweight observables framework, also available in Kotlin Installation Carthage You can install Carthage with Homebrew using the followin

VueFlux is the architecture to manage state with unidirectional data flow for Swift, inspired by Vuex and Flux.
VueFlux is the architecture to manage state with unidirectional data flow for Swift, inspired by Vuex and Flux.

Unidirectional State Management Architecture for Swift - Inspired by Vuex and Flux Introduction VueFlux is the architecture to manage state with unidi

EventBroadcaster is a lightweight event handler framework, written in swift for iOS, macOS, tvOS & watchOS applications.
EventBroadcaster is a lightweight event handler framework, written in swift for iOS, macOS, tvOS & watchOS applications.

EventBroadcaster is a lightweight event handler framework, written in swift for iOS, macOS, tvOS & watchOS applications.

A New, Modern Reactive State Management Library for Swift and SwiftUI (The iOS implementation of Recoil)
A New, Modern Reactive State Management Library for Swift and SwiftUI (The iOS implementation of Recoil)

RecoilSwift RecoilSwift is a lightweight & reactive swift state management library. RecoilSwift is a SwiftUI implementation of recoil.js which powered

Easy Swift Futures & Promises.

❗️ Archived now ❗️ Since Apple released Combine framework, I decide to archive this repo. You still can use this repo as an example of Future/Promise

Write great asynchronous code in Swift using futures and promises

BrightFutures How do you leverage the power of Swift to write great asynchronous code? BrightFutures is our answer. BrightFutures implements proven fu

FutureLib is a pure Swift 2 library implementing Futures & Promises inspired by Scala.

FutureLib FutureLib is a pure Swift 2 library implementing Futures & Promises inspired by Scala, Promises/A+ and a cancellation concept with Cancellat

Write great asynchronous code in Swift using futures and promises

BrightFutures How do you leverage the power of Swift to write great asynchronous code? BrightFutures is our answer. BrightFutures implements proven fu

Async+ for Swift provides a simple chainable interface for your async and throwing code, similar to promises and futures
Async+ for Swift provides a simple chainable interface for your async and throwing code, similar to promises and futures

Async+ for Swift provides a simple chainable interface for your async and throwing code, similar to promises and futures. Have the best of both worlds

Futures and Promises library

#PureFutures A simple Futures and Promises library. ##Installation ###Carthage Add the following in your Cartfile: github "wiruzx/PureFutures" And ru

Futures is a cross-platform framework for simplifying asynchronous programming, written in Swift.

Futures Futures is a cross-platform framework for simplifying asynchronous programming, written in Swift. It's lightweight, fast, and easy to understa

Hydra ⚡️ Lightweight full-featured Promises, Async & Await Library in Swift
Hydra ⚡️ Lightweight full-featured Promises, Async & Await Library in Swift

Lightweight full-featured Promises, Async & Await Library in Swift What's this? Hydra is full-featured lightweight library which allows you to write b

A Swift based Future/Promises Library for IOS and OS X.

FutureKit for Swift A Swift based Future/Promises Library for IOS and OS X. Note - The latest FutureKit is works 3.0 For Swift 2.x compatibility use v

Lightweight Promises for Swift & Obj-C

Tomorrowland Tomorrowland is an implementation of Promises for Swift and Objective-C. A Promise is a wrapper around an asynchronous task that provides

When is a lightweight implementation of Promises in Swift
When is a lightweight implementation of Promises in Swift

Description When is a lightweight implementation of Promises in Swift. It doesn't include any helper functions for iOS and OSX and it's intentional, t

Promises for Swift & ObjC.
Promises for Swift & ObjC.

Promises simplify asynchronous programming, freeing you up to focus on the more important things. They are easy to learn, easy to master and result in

Comments
  • How to continue promise-future chain on error?

    How to continue promise-future chain on error?

    Example:

    let promise = Promise<Void>()
    Dispatch.global.async {
      ... //fulfill promise with success or error
    }
    promise.future.onError { error in
      ...//some async actions
    }
    

    How to append "some async actions" to the action chain correctly if they are executed in error case only?

    opened by gerchicov-bp 0
Releases(1.2.0)
Owner
Dmytro Mishchenko
Dmytro Mishchenko
A Swift based Future/Promises Library for IOS and OS X.

FutureKit for Swift A Swift based Future/Promises Library for IOS and OS X. Note - The latest FutureKit is works 3.0 For Swift 2.x compatibility use v

null 759 Dec 2, 2022
Lightweight Promises for Swift & Obj-C

Tomorrowland Tomorrowland is an implementation of Promises for Swift and Objective-C. A Promise is a wrapper around an asynchronous task that provides

Lily Ballard 115 Nov 23, 2022
When is a lightweight implementation of Promises in Swift

Description When is a lightweight implementation of Promises in Swift. It doesn't include any helper functions for iOS and OSX and it's intentional, t

Vadym Markov 259 Dec 29, 2022
Promises simplify asynchronous programming, freeing you up to focus on the more important things

Promises simplify asynchronous programming, freeing you up to focus on the more important things. They are easy to learn, easy to master and result in

Max Howell 14k Jan 5, 2023
TopicEventBus is Easy to use, type safe way of implementing Publish–subscribe design pattern.

TopicEventBus Publish–subscribe design pattern implementation framework, with ability to publish events by topic. (NotificationCenter extended alterna

Matan Abravanel 55 Nov 29, 2021
Bond is a Swift binding framework that takes binding concepts to a whole new level.

Bond, Swift Bond Update: Bond 7 has been released! Check out the migration guide to learn more about the update. Bond is a Swift binding framework tha

Declarative Hub 4.2k Jan 5, 2023
Type-safe event handling for Swift

emitter-kit v5.2.2 A replacement for NSNotificationCenter#addObserver and NSObject#addObserver that is type-safe and not verbose. import EmitterKit /

Alec Larson 570 Nov 25, 2022
Swift Apps in a Swoosh! A modern framework for creating iOS apps, inspired by Redux.

Katana is a modern Swift framework for writing iOS applications' business logic that are testable and easy to reason about. Katana is strongly inspire

Bending Spoons 2.2k Jan 1, 2023
📡 Helping you own NotificationCenter in Swift!

Notificationz ?? Helping you own NotificationCenter Highlights Keep Your Naming Conventions: This library gives you convenient access to NotificationC

Kitz 77 Feb 18, 2022
Observable is the easiest way to observe values in Swift.

Observable is the easiest way to observe values in Swift. How to Create an Observable and MutableObservable Using MutableObservable you can create and

Robert-Hein Hooijmans 368 Nov 9, 2022