When is a lightweight implementation of Promises in Swift

Related tags

Event swift promise
Overview

When

CI Status Version Carthage Compatible License Platform

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, to remove redundant complexity and give you more freedom and flexibility in your choices. It is type safe, thanks Swift generics, so you could create promises with whatever type you want.

When can easily be integrated into your projects and libraries to move your asynchronous code up to the next level.

Table of Contents

When Icon

Why?

To make asynchronous code more readable and standardized:

fetchJSON().then({ data: NSData -> [[String: AnyObject]] in
  // Convert to JSON
  return json
}).then({ json: [[String: AnyObject]] -> [Entity] in
  // Map JSON
  // Save to database
  return items
}).done({ items: [Entity] in
  self.items = items
  self.tableView.reloadData()
}).error({ error in
  print(error)
})

Usage

Promise

A promise represents the future value of a task. Promises start in a pending state and then could be resolved with a value or rejected with an error.

// Creates a new promise that could be resolved with a String value
let promise = Promise<String>()
// Resolves the promise
promise.resolve("String")
// Or rejects the promise
promise.reject(Error.notFound)
// Creates a new promise that is resolved with a String value
let promise = Promise({
  return "String"
})
// Creates a new promise that is rejected with an Error
let promise = Promise({
  //...
  throw Error.notFound
})

Callbacks of the current promise and all the chained promises (created with then) are executed on the main queue by default, but you can always specify the needed queue in init:

let promise = Promise<String>(queue: dispatch_get_main_queue())

Done

Adds a handler to be called when the promise object is resolved with a value:

// Create a new promise in a pending state
let promise = Promise<String>()
// Add done callback
promise.done({ value in
  print(value)
})
// Resolve the promise
promise.resolve("String")

Fail

Adds a handler to be called when the promise object is rejected with an Error:

// Create a new promise in a pending state
let promise = Promise<String>()
// Add fail callback
promise.fail({ error in
  print(error)
})
// Reject the promise
promise.reject(Error.notFound)

It's also possible to cancel a promise, which means it will be rejected with PromiseError.cancelled error. FailurePolicy can be used if you want to ignore this error in your fail handler:

// Create a new promise in a pending state
let promise = Promise<String>()
// This callback will not be called when a promise is cancelled
promise.fail({ error in
  print(error)
})
// This callback will be called when a promise is cancelled
promise.fail(policy: .allErrors, { error in
  print(error)
})
// Cancel the promise
promise.cancel()

Always

Adds a handler to be called when the promise object is either resolved or rejected. This callback will be called after done or fail handlers.

// Create a new promise in a pending state
let promise = Promise<String>()
// Add always callback
promise.always({ result in
  switch result {
  case let .success(value):
    print(value)
  case let .failure(error):
    print(error)
  }
})
// Resolve or reject the promise
promise.resolve("String") // promise.reject(Error.notFound)

Then

Returns a new promise that can use the result value of the current promise. It means that you could easily create chains of promises to simplify complex asynchronous operations into clear and simple to understand logic.

A new promise is resolved with the value returned from the provided closure:

let promise = Promise<NSData>()

promise
  .then({ data -> Int in
    return data.length
  }).then({ length -> Bool in
    return length > 5
  }).done({ value in
    print(value)
  })

promise.resolve("String".dataUsingEncoding(NSUTF8StringEncoding)!)

A new promise is resolved when the promise returned from the provided closure resolves:

struct Networking {
  static func GET(url: NSURL) -> Promise {
    let promise = Promise<NSData>()
    //...
    return promise
  }
}

Networking.GET(url1)
  .then({ data -> Promise<NSData> in
    //...
    return Networking.GET(url2)
  }).then({ data -> Int in
    return data.length
  }).done({ value in
    print(value)
  })

then closure is executed on the main queue by default, but you can pass a needed queue as a parameter:

promise.then(on: dispatch_get_global_queue(QOS_CLASS_UTILITY, 0))({ data -> Int in
  //...
})

If you want to use background queue there are the helper methods for this case:

promise1.thenInBackground({ data -> Int in
  //...
})

promise2.thenInBackground({ data -> Promise<NSData> in
  //...
})

Recover

Returns a new promise that can be used to continue the chain when an error was thrown.

let promise = Promise<String>()
// Recover the chain
promise
  .recover({ error -> Promise<String> in
    return Promise({
      return "Recovered"
    })
  })
  .done({ string in
    print(string) // Recovered
  })
// Reject the promise
promise.reject(Error.notFound)

When

Provides a way to execute callback functions based on one or more promises. The when method returns a new "master" promise that tracks the aggregate state of all the passed promises. The method will resolve its "master" promise as soon as all the promises resolve, or reject the "master" promise as soon as one of the promises is rejected. If the "master" promise is resolved, the done callback is executed with resolved values for each of the promises:

let promise1 = Promise<Int>()
let promise2 = Promise<String>()
let promise3 = Promise<Int>()

when(promise1, promise2, promise3)
  .done({ value1, value2, value3 in
    print(value1)
    print(value2)
    print(value3)
  })

promise1.resolve(1)
promise2.resolve("String")
promise3.resolve(3)

Reactive extensions

Use the following extension in order to integrate When with RxSwift:

import RxSwift

extension Promise: ObservableConvertibleType {
  public func asObservable() -> Observable {
    return Observable.create({ observer in
      self
        .done({ value in
          observer.onNext(value)
        })
        .fail({ error in
          observer.onError(error)
        })
        .always({ _ in
          observer.onCompleted()
        })

      return Disposables.create()
    })
  }
}

Installation

When is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'When'

For RxSwift extensions you can use CocoaPods subspecs:

pod 'When/RxSwift'

When is also available through Carthage. To install just write into your Cartfile:

github "vadymmarkov/When"

Author

Vadym Markov, [email protected]

Credits

Credits for inspiration go to PromiseKit and Then.

Contributing

Check the CONTRIBUTING file for more info.

License

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

Comments
  • When.BarrierQueue

    When.BarrierQueue

    Hello! I found what barrier queue is concurrent:

    Functions.swift:

    let barrierQueue = DispatchQueue(label: "When.BarrierQueue", attributes: DispatchQueue.Attributes.concurrent)
    

    But it must be serial to behave as real barrier. Misprint?

    opened by MontakOleg 5
  • WhenRx as an optional dependency?

    WhenRx as an optional dependency?

    Is there any way to include When as a dependency via Carthage and have it not pull down RxSwift et al? I won't be using that part of the library and it basically introduces a huge overhead to my Cartfile checkouts and builds otherwise.

    opened by timsearle 4
  • Added 'When' dependency to RxWhen-tvOS target

    Added 'When' dependency to RxWhen-tvOS target

    Setup: Carthage only

    Issue: When integrating When using carthage bootstrap, and not specifying the platforms, the build fails.

    Reason: Promise+Rx.swift requires the When module, to which the linking was not included in the target and thus breaking the build.

    If this was deliberate, and the change I propose is breaking/I have to go another route to solve this issue, please say so.


    Disclaimer: I have never developed for tvOS, and am not aware of any restrictions that might be triggered by this, but given the fact that iOS works in the exact same way, and 'Allow app extension API only' is deselected, I have no reason to assume it'll fail.

    opened by daneov 2
  • Default DispatchQueue

    Default DispatchQueue

    Maybe queue should be optional? Regularly I want execute promise callback in same queue where promise was created. I want set queue explicitly for async operations. Now i am change default queue from .main to 'instant', but i want rework dispatch in my fork in future. If you agreed with me i can make PR.

    Sorry for bad english.

    opened by ghfghfg23 2
  • Add @discardableResult to promises that run in different queues

    Add @discardableResult to promises that run in different queues

    This fixes warnings that occur when you use promises with dispatch queues. The fix is to add @discardableResult so that you don't have to keep a reference of the promise result.

    opened by zenangst 2
  • Calling 'when' with more than 3 promises

    Calling 'when' with more than 3 promises

    I stumbled upon a case where I need to pass 4 promises to the when function, and they all return different types. Therefore I can't use the function that receives an Array<T>.

    Is there a specific reason to limit the function to 3 promises? Or is it just because it would need to repeat pretty much similar pieces of code for each number of inputs?

    opened by guilhermearaujo 1
  • Aligned iOS deployment target with targets

    Aligned iOS deployment target with targets

    Both targets

    • When-iOS
    • RxWhen-iOS

    Both target iOS 8, as does the downstream dependency RxSwift, but the project's iOS Deployment Target was set to 9.2. This was brought to my attention when building hyperoslo/Malibu with Xcode 10.

    This PR aligns those versions.

    opened by daneov 1
  • Duplicate bundle identifier for When and RxWhen

    Duplicate bundle identifier for When and RxWhen

    When and RxWhen both have the same bundle identifier which prevents using both at the same time. Also, RxWhen depends on When, which means both are meant to be used at the same time.

    error

    Proposed Solution: Rename the Bundle identifiers for RxWhen to RxWhen-iOS, RxWhen-Mac, etc.

    opened by ludovicarnold 1
  • Added Linux Support

    Added Linux Support

    Tests won't be possible quite yet cos Quick/Nimble haven't updated their Package.swift files quite yet, but I can confirm building works.

    In case you'd like to check it out, I'd suggest downloading Docker for mac and running

    1. docker run -it --privileged --volume (pwd)":/package" ibmcom/swift-ubuntu:4.0.3 /bin/bash to enter a shell
    2. cd package (that's the directory we mounted)
    3. swift build (try writing a script or 2 to make sure nothing's borked 😉 )
    opened by codeOfRobin 1
  • Done handlers not called if promise already resolved

    Done handlers not called if promise already resolved

    This code will print 5:

    let promise = Promise<Int>(queue: DispatchQueue.global())
    promise.done { result in
        print(result)
    }
    promise.resolve(5)
    

    But this is not:

    let promise = Promise<Int>(queue: DispatchQueue.global())
    promise.resolve(5)
    promise.done { result in
        print(result)
    }
    

    This behavior is by design?

    opened by MontakOleg 1
  • Promise: Make cancel an open function

    Promise: Make cancel an open function

    This change opens up cancel() to be overriden in other modules. This will be used in the Malibu framework (https://github.com/hyperoslo/malibu) to be able to use the latest version of When.

    opened by JohnSundell 1
Releases(5.0.0)
Owner
Vadym Markov
iOS Software Engineer
Vadym Markov
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

Thomas Visser 1.9k Dec 20, 2022
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

Dmytro Mishchenko 40 Sep 23, 2022
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
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
📬 A lightweight implementation of an observable sequence that you can subscribe to.

Features Lightweight Observable is a simple implementation of an observable sequence that you can subscribe to. The framework is designed to be minima

Felix M. 133 Aug 17, 2022
RxReduce is a lightweight framework that ease the implementation of a state container pattern in a Reactive Programming compliant way.

About Architecture concerns RxReduce Installation The key principles How to use RxReduce Tools and dependencies Travis CI Frameworks Platform Licence

RxSwift Community 125 Jan 29, 2022
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.

null 7.3k Jan 9, 2023
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

Holly Li 160 Dec 25, 2022
Open source implementation of Apple's Combine framework for processing values over time.

OpenCombine Open-source implementation of Apple's Combine framework for processing values over time. The main goal of this project is to provide a com

OpenCombine 2.4k Dec 26, 2022
Very simple Observable and Publisher implementation for iOS apps.

Very simple Observable and Publisher implementation for iOS apps.

Igor Kulman 7 Jun 11, 2022
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.

Ali Samaiee 4 Oct 5, 2022
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
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

Postmates Inc. 708 Jun 29, 2022
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

ReactorKit 2.5k Dec 28, 2022
🐌 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

Compass 179 Nov 21, 2022