• Stars
    star
    110
  • Rank 316,713 (Top 7 %)
  • Language
    Swift
  • License
    MIT License
  • Created about 7 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

The easiest Future and Promises framework in Swift. No magic. No boilerplate.

Promis

Build Status Version License Platform

The easiest Future and Promises framework in Swift. No magic. No boilerplate.

Overview

While starting from the Objective-C implementation of JustPromises and keeping the code minimalistic, this library adds the following:

  • conversion to Swift 4
  • usage of generics to allow great type inference that wasn't possible in Objective-C
  • overall refactoring for fresh and modern code
  • remove the unnecessary and misleading concept of Progress causing bad patterns to emerge

You can read about the theory behind Future and Promises on Wikipedia, here are the main things you should know to get started.

  • Promises represent the promise that a task will be fulfilled in the future while the future holds the state of such resolution.
  • Futures, when created are in the unresolved state and can be resolved with one of 3 states: with a result, an error, or being cancelled.
  • Futures can be chained, allowing to avoid the pyramid of doom problem, clean up asynchronous code paths and simplify error handling.

Promis brags about being/having:

  • Fully unit-tested and documented 💯
  • Thread-safe 🚦
  • Clean interface 👼
  • Support for chaining ⛓
  • Support for cancellation 🙅‍♂️
  • Queue-based block execution if needed 🚆
  • Result type provided via generics 🚀
  • Keeping the magic to the minimum, leaving the code in a readable state without going off of a tangent with fancy and unnecessary design decisions ಠ_ಠ

Alternatives

Other open-source solutions exist such as:

Promis takes inspiration from the Objective-C version of JustPromises developed by the iOS Team of Just Eat which is really concise and minimalistic, while other libraries are more weighty.

Usage

The following example should outline the main benefits of using futures via chaining.

let request = URLRequest(url: URL(string: "http://example.com")!)

// starts by hitting an API to download data
getData(request: request).thenWithResult { data in
    // continue by parsing the retrieved data
    parse(data: data)
}.thenWithResult { parsedData in
    // continue by mapping the parsed data
    map(data: parsedData)
}.onError { error in
    // executed only in case an error occurred in the chain
    print("error: " + String(describing: error))
}.finally(queue: .main) { future in
    // always executed, no matter the state of the previous future or how the chain did perform
    switch future.state {
        case .result(let value):
            print(String(describing: value))
        case .error(let err):
            print(String(describing: err))
        case .cancelled:
            print("future is in a cancelled state")
        case .unresolved:
            print("this really cannot be if any chaining block is executed")
        }
}

The functions used in the example have the following signatures:

func getData(request: URLRequest) -> Future<Data>
func parse(data: Data) -> Future<[Dictionary<String,AnyObject>]>
func map(data: [Dictionary<String,AnyObject>]) -> Future<[FooBar]>

Promises and Futures are parametrized leveraging the power of the generics, meaning that Swift can infer the type of the result compile type. This was a considerable limitation in the Objective-C world and we can now prevent lots of issues at build time thanks to the static typing nature of the language. The state of the future is an enum defined as follows:

enum FutureState<ResultType> {
    case unresolved
    case result(ResultType)
    case error(Error)
    case cancelled
}

Promises are created and resolved like so:

let promise = Promise<ResultType>()
promise.setResult(value)
// or
promise.setError(error)
// or
promise.cancel()

Continuation methods used for chaining are the following:

func then<NextResultType>(queue: DispatchQueue? = nil, task: @escaping (Future) -> Future<NextResultType>) -> Future<NextResultType>
func thenWithResult<NextResultType>(queue: DispatchQueue? = nil, continuation: @escaping (ResultType) -> Future<NextResultType>) -> Future<NextResultType> {
func onError(queue: DispatchQueue? = nil, continuation: @escaping (Error) -> Void) -> Future {
func finally(queue: DispatchQueue? = nil, block: @escaping (Future<ResultType>) -> Void)

All the functions can accept an optional DispatchQueue used to perform the continuation blocks.

Best practices

Functions wrapping async tasks should follow the below pattern:

func wrappedAsyncTask() -> Future<ResultType> {

    let promise = Promise<Data>()
    someAsyncOperation() { data, error in
        // resolve the promise according to how the async operations did go
        switch (data, error) {
        case (let data?, _):
            promise.setResult(data)
        case (nil, let error?):
            promise.setError(error)
        // etc.
        }
    }
    return promise.future
}

You could chain an onError continuation before returning the future to allow in-line error handling, which I find to be a very handy pattern.

// ...
return promise.future.onError {error in
    // handle/log error
}

Pitfalls

When using then or thenWithResult, the following should be taken in consideration.

...}.thenWithResult { data -> Future<NextResultType> in
    /**
    If a block is not trivial, Swift cannot infer the type of the closure and gives the error
    'Unable to infer complex closure return type; add explicit type to disambiguate'
    so you'll have to add `-> Future<NextResultType> to the block signature
    
    You can make the closure complex just by adding any extra statement (like a print).
    
    All the more reason to structure your code as done in the first given example :)
    */
    print("complex closure")
    return parse(data: data)
}

Please check the GettingStarted playground in the demo app to see the complete implementation of the above examples.

Installation

CocoaPods

Add Promis to your Podfile

use_frameworks!
target 'MyTarget' do
    pod 'Promis', '~> x.y.z'
end
$ pod install

Carthage

github "albertodebortoli/Promis" ~> "x.y.z"

Then on your application target Build Phases settings tab, add a "New Run Script Phase". Create a Run Script with the following content:

/usr/local/bin/carthage copy-frameworks

and add the following paths under "Input Files":

$(SRCROOT)/Carthage/Build/iOS/Promis.framework

Author

Alberto De Bortoli [email protected] Twitter: @albertodebo GitHub: albertodebortoli website: albertodebortoli.com

License

Promis is available under the Apache 2 license in respect of JustPromises which this library takes inspiration from. See the LICENSE file for more info.

More Repositories

1

Skopelos

A minimalistic, thread safe, non-boilerplate and super easy to use version of Active Record on Core Data. Simply all you need for doing Core Data. Swift flavour.
Swift
236
star
2

GoldRaccoon

The iOS component to connect to a FTP service and perform the operations you need. http://albertodebortoli.github.io/GoldRaccoon/
Objective-C
159
star
3

Stateful

A minimalistic, thread-safe, non-boilerplate and super easy to use state machine in Swift.
Swift
96
star
4

ADBIndexedTableView

Indexed UITableView using first letter objects property.
Objective-C
59
star
5

ADBActors

Simple concept of Actor Model in Objective-C based on the idea of Valletta Ventures Actors library.
Objective-C
58
star
6

ADBBackgroundCells

ADBBackgroundCells allow lazy loading for UITableViewCell objects performing a long time job in background without blocking the UI.
Objective-C
42
star
7

Bitlyzer

Class to shorten URLs with Bit.ly on iOS (both block based and delegate based using ARC)
Objective-C
41
star
8

ADBDownloadManager

A download manager for iOS. Actually, all that you need to download files without any external library.
Objective-C
33
star
9

ADBGridView

ADBGridView inherits from UITableView and is populated with ADBImageViews (https://github.com/albertodebortoli/ADBImageView). Number of images for row (cells) can be customized. UITableView is inherited to use cell reuse facility.
Objective-C
24
star
10

Skiathos

A minimalistic, thread safe, non-boilerplate and super easy to use version of Active Record on Core Data. Simply all you need for doing Core Data. Objective-C flavour.
Objective-C
19
star
11

ModulePods

Toolbar app to easily run common CocoaPods commands
Swift
12
star
12

rubiks-cube-solution

The simplest, easiest and quickest way to learn how to solve the Rubik's Cube.
10
star
13

ADBStateMachine

A proper thread-safe state machine for Objective-C.
Objective-C
10
star
14

WWDC-2016-Apple-Logo

Swift
6
star
15

ImageAnalysisFilters

A simple C++ project for applying filters to raw images via command line.
C++
4
star
16

Introspecta

Utilities for introspection based on Objective-C runtime
Objective-C
4
star
17

ADBImageView

Asynchronous Image View for iOS providing placeholder, activity indicator, gestures and caching. Delegation and ARC based.
Objective-C
4
star
18

ADBCategories

Useful categories for iOS with ARC
Objective-C
3
star
19

Uncrustify-ObjC

Uncrustify configuration file for Objective-C files.
2
star
20

ADBReasonableTextView

A UITextView replacement with reasonable delegate methods.
Objective-C
2
star
21

iOSLab-DigitalAccademia-2012

Digital Accademia iOS Lab held in October 2012
Objective-C
2
star
22

iVIP

iVIP is an Xcode template project that allows quick creation of applications focused on a single, maybe famous, person.
Objective-C
2
star
23

EC2macConnector

EC2macConnector is a CLI tool that helps connect to EC2 Mac instances easily.
Swift
2
star
24

BMYSmartFeed

Facebook-style iOS feed architecture
Objective-C
2
star
25

the_coding_love

Simple iOS third-party reader for the_coding_love(); that shows GIFs when they are fully loaded.
Objective-C
1
star
26

iHarmonyDB

The DB powering iHarmony
1
star
27

ADBLanguageManager

A reusable localization manager class for iOS by Toni Sala and Alberto De Bortoli
Objective-C
1
star
28

LBDelegateMatrioska

Multiple delegates by using NSProxy
Objective-C
1
star
29

RuzzleSolver

A quick and dirty Ruzzle solver in Ruby using Ukkonen's suffix tree algorithm.
Ruby
1
star
30

Xcode-4-Themes

Xcode 4 Themes I use
1
star
31

ADBObjectLocker

Prototype. Idea. Maybe pointless. Maybe just cool mental gymnastic. Maybe I should just drink less beer. Class to handle lock on specific methods for specific instances.
Objective-C
1
star