Tame async code with battle-tested promises

Overview

Then

Then

Language: Swift 5 Platform: iOS 8+/macOS10.11 SPM compatible Carthage compatible Cocoapods compatible Build Status codebeat badge License: MIT Release version

Reason - Example - Documentation - Installation

fetchUserId().then { id in
    print("UserID : \(id)")
}.onError { e in
    print("An error occured : \(e)")
}.finally {
    print("Everything is Done :)")
}
  let userId = try! await(fetchUserId())

Because async code is hard to write, hard to read, hard to reason about. A pain to maintain

Try it

then is part of freshOS iOS toolset. Try it in an example App! Download Starter Project

How

By using a then keyword that enables you to write aSync code that reads like an English sentence
Async code is now concise, flexible and maintainable ❤️

What

  • Based on the popular Promise / Future concept
  • Async / Await
  • progress race recover validate retry bridgeError chain noMatterWhat ...
  • Strongly Typed
  • Pure Swift & Lightweight

Example

Before

fetchUserId({ id in
    fetchUserNameFromId(id, success: { name in
        fetchUserFollowStatusFromName(name, success: { isFollowed in
            // The three calls in a row succeeded YAY!
            reloadList()
        }, failure: { error in
            // Fetching user ID failed
            reloadList()
        })
    }, failure: { error in
        // Fetching user name failed
        reloadList()
    })
}) {  error in
    // Fetching user follow status failed
    reloadList()
}
🙉🙈🙊#callbackHell

After

fetchUserId()
    .then(fetchUserNameFromId)
    .then(fetchUserFollowStatusFromName)
    .then(updateFollowStatus)
    .onError(showErrorPopup)
    .finally(reloadList)

🎉 🎉 🎉

Going further 🤓

fetchUserId().then { id in
    print("UserID : \(id)")
}.onError { e in
    print("An error occured : \(e)")
}.finally {
    print("Everything is Done :)")
}

If we want this to be maintainable, it should read like an English sentence
We can do this by extracting our blocks into separate functions:

fetchUserId()
    .then(printUserID)
    .onError(showErrorPopup)
    .finally(reloadList)

This is now concise, flexible, maintainable, and it reads like an English sentence <3
Mental sanity saved // #goodbyeCallbackHell

Documentation

  1. Writing your own Promise
  2. Progress
  3. Registering a block for later
  4. Returning a rejecting promise
  5. Common Helpers
  6. race
  7. recover
  8. validate
  9. retry
  10. bridgeError
  11. whenAll
  12. chain
  13. noMatterWhat
  14. unwrap
  15. AsyncTask
  16. Async/Await

Writing your own Promise 💪

Wondering what fetchUserId() is?
It is a simple function that returns a strongly typed promise :

func fetchUserId() -> Promise<Int> {
    return Promise { resolve, reject in
        print("fetching user Id ...")
        wait { resolve(1234) }
    }
}

Here you would typically replace the dummy wait function by your network request <3

Progress

As for then and onError, you can also call a progress block for things like uploading an avatar for example.

uploadAvatar().progress { p in
  // Here update progressView for example
}
.then(doSomething)
.onError(showErrorPopup)
.finally(doSomething)

Registering a block for later

Our implementation slightly differs from the original javascript Promises. Indeed, they do not start right away, on purpose. Calling then, onError, or finally will start them automatically.

Calling then starts a promise if it is not already started. In some cases, we only want to register some code for later. For instance, in the case of JSON to Swift model parsing, we often want to attach parsing blocks to JSON promises, but without starting them.

In order to do that we need to use registerThen instead. It's the exact same thing as then without starting the promise right away.

let fetchUsers:Promise<[User]> = fetchUsersJSON().registerThen(parseUsersJSON)

// Here promise is not launched yet \o/

// later...
fetchUsers.then { users in
    // YAY
}

Note that onError and finally also have their non-starting counterparts : registerOnError and registerFinally.

Returning a rejecting promise

Oftetimes we need to return a rejecting promise as such :

return Promise { _, reject in
  reject(anError)
}

This can be written with the following shortcut :

return Promise.reject(error:anError)

Common Helpers

Race

With race, you can send multiple tasks and get the result of the first one coming back :

race(task1, task2, task3).then { work in
  // The first result !
}

Recover

With .recover, you can provide a fallback value for a failed Promise.
You can :

  • Recover with a value
  • Recover with a value for a specific Error type
  • Return a value from a block, enabling you to test the type of error and return distinct values.
  • Recover with another Promise with the same Type
.recover(with: 12)
.recover(MyError.defaultError, with: 12)
.recover { e in
  if e == x { return 32 }
  if e == y { return 143 }
  throw MyError.defaultError
}
.recover { e -> Promise<Int> in
  // Deal with the error then
  return Promise<Int>.resolve(56)
  // Or
  return Promise<Int>.reject(e)
  }
}
.recover(with: Promise<Int>.resolve(56))

Note that in the block version you can also throw your own error \o/

Validate

With .validate, you can break the promise chain with an assertion block.

You can:

  • Insert assertion in Promise chain
  • Insert assertion and return you own Error

For instance checking if a user is allowed to drink alcohol :

fetchUserAge()
.validate { $0 > 18 }
.then { age in
  // Offer a drink
}

.validate(withError: MyError.defaultError, { $0 > 18 })`

A failed validation will retrun a PromiseError.validationFailed by default.

Retry

With retry, you can restart a failed Promise X number of times.

doSomething()
  .retry(10)
  .then { v in
   // YAY!
  }.onError { e in
    // Failed 10 times in a row
  }

BridgeError

With .bridgeError, you can intercept a low-level Error and return your own high level error. The classic use-case is when you receive an api error and you bridge it to your own domain error.

You can:

  • Catch all errors and use your own Error type
  • Catch only a specific error
.bridgeError(to: MyError.defaultError)
.bridgeError(SomeError, to: MyError.defaultError)

WhenAll

With .whenAll, you can combine multiple calls and get all the results when all the promises are fulfilled :

whenAll(fetchUsersA(),fetchUsersB(), fetchUsersC()).then { allUsers in
  // All the promises came back
}

Chain

With chain, you can add behaviours without changing the chain of Promises.

A common use-case is for adding Analytics tracking like so:

extension Photo {
    public func post() -> Async
    {
        
   return api.
   post(
   self).
   chain { 
   _ 
   in
            Tracker.
   trackEvent(.
   postPicture)
        }
    }
}
  

NoMatterWhat

With noMatterWhat you can add code to be executed in the middle of a promise chain, no matter what happens.

func fetchNext() -> Promise<[T]> {
    isLoading = true
    call.params["page"] = page + 1
    return call.fetch()
        .registerThen(parseResponse)
        .resolveOnMainThread()
        .noMatterWhat {
            self.isLoading = false
    }
}

Unwrap

With unwrap you can transform an optional into a promise :

func fetch(userId: String?) -> Promise<Void> {
   return unwrap(userId).then {
        network.get("/user/\($0)")
    }
}

Unwrap will fail the promise chain with unwrappingFailed error in case of a nil value :)

AsyncTask

AsyncTask and Async typealisases are provided for those of us who think that Async can be clearer than Promise. Feel free to replace Promise by AsyncTask and Promise by Async wherever needed.
This is purely for the eyes :)

Async/Await

await waits for a promise to complete synchronously and yields the result :

let photos = try! await(getPhotos())

async takes a block and wraps it in a background Promise.

async {
  let photos = try await(getPhotos())
}

Notice how we don't need the ! anymore because async will catch the errors.

Together, async/await enable us to write asynchronous code in a synchronous manner :

async {
  let userId = try await(fetchUserId())
  let userName = try await(fetchUserNameFromId(userId))
  let isFollowed = try await(fetchUserFollowStatusFromName(userName))
  return isFollowed
}.then { isFollowed in
  print(isFollowed)
}.onError { e in
  // handle errors
}

Await operators

Await comes with .. shorthand operator. The ..? will fallback to a nil value instead of throwing.

let userId = try await(fetchUserId())

Can be written like this:

let userId = try ..fetchUserId()

Installation

The Swift Package Manager (SPM) is now the official way to install Then. The other package managers are now deprecated as of 5.1.3 and won't be supported in future versions.

Swift Package Manager

Xcode > File > Swift Packages > Add Package Dependency... > Paste https://github.com/freshOS/Then

Cocoapods - Deprecated

target 'MyApp'
pod 'thenPromise'
use_frameworks!

Carthage - Deprecated

github "freshOS/then"

Contributors

S4cha, Max Konovalov, YannickDot, Damien, piterlouis

Swift Version

  • Swift 2 -> version 1.4.2
  • Swift 3 -> version 2.2.5
  • Swift 4 -> version 3.1.0
  • Swift 4.1 -> version 4.1.1
  • Swift 4.2 -> version 4.2.0
  • Swift 4.2.1 -> version 4.2.0
  • Swift 5.0 -> version 5.0.0
  • Swift 5.1 -> version 5.1.0
  • Swift 5.1.3 -> version 5.1.2

Backers

Like the project? Offer coffee or support us with a monthly donation and help us continue our activities :)

Sponsors

Become a sponsor and get your logo on our README on Github with a link to your site :)

Comments
  • Resolve compile issue on Xcode 9.3

    Resolve compile issue on Xcode 9.3

    The swift compiler was unable to resolve the overloaded convenience initializer. Being more explicit by marking the desired initializer with void: allows the compiler to successfully complete it's build. Resolves #37

    opened by casademora 18
  • Promise does not deinit

    Promise does not deinit

    In Promise.swift add debug outputs

        public init(callback: @escaping (_ resolve: @escaping ResolveCallBack,
            _ reject: @escaping RejectCallBack) -> Void) {
            promiseCallBack = callback
            print("+")
        }
    
        public init(callback: @escaping (_ resolve: @escaping ResolveCallBack,
            _ reject: @escaping RejectCallBack, _ progress: @escaping ProgressCallBack) -> Void) {
            promiseProgressCallBack = callback
            print("+")
        }
    
        deinit { print("-") }
    

    Then if you run

    func foo() {
        let a = Promise { r, _ in r() }
        let b = Promise { r, _ in r() }
        a.then{ print(".") }.then(b).then{ print(".") }
    }
    foo()
    

    The output will be

    +
    +
    +
    .
    +
    +
    +
    +
    .
    

    After adding [weak self] to all registerThen<X> blocks

    let p = Promise<X> { [weak self] resolve, reject in
    

    and changing passAlongFirstPromiseStartFunctionAndStateTo<X> to

            } else {
                promise.initialPromiseStart = { [weak self] self?.start() }
            }
    

    The output:

    +
    +
    +
    .
    +
    +
    +
    +
    .
    -
    -
    

    There are still strong ref cycles somewhere, any ideas?

    opened by mandarin6b0 15
  • Package.swift needs upgrade for swift 4.

    Package.swift needs upgrade for swift 4.

    I tried to use 3.0.0 in a project, compiling with Swift 4.0 from command line ... Got:

    .../Source/Promise.swift:204:9: error: 'promiseProgressCallBack' is inaccessible due to 'private' protection level
            promiseProgressCallBack = { resolve, reject, progress in
            ^
    
    .../Promise.swift:39:17: note: 'promiseProgressCallBack' declared here
        private var promiseProgressCallBack: ((_ resolve: @escaping ((T) -> Void),
                    ^
    
    .../Promise.swift:218:9: error: 'promiseProgressCallBack' is inaccessible due to 'private' protection level
            promiseProgressCallBack = { resolve, reject, progress in
            ^
    
    .../Promise.swift:39:17: note: 'promiseProgressCallBack' declared here
        private var promiseProgressCallBack: ((_ resolve: @escaping ((T) -> Void),
    

    It seems if you're using swift 4 toolchain, when you build 'then' from command line, it's built using swift 3 mode due to old manifest file format. Since Swift 4 changed access control rules the errors above appear.

    When I changed 'then' Package.swift to:

    // swift-tools-version:4.0
    import PackageDescription
    
    let package = Package(
       name: "then",
       products: [
          .library(
             name: "then",
             targets: ["then"]),
          ],
       dependencies: [
       ],
       targets: [
          .target(
             name: "then",
             dependencies: [])
          ]
    )
    

    Project built with no errors.

    I created a new subfolder 'then' inside Sources and moved the source files to the folder to be able to compile the code, since new package manager requires it... (I tried with path property but with no success)

    path: This property defines the path to the top-level directory containing the target's sources, relative to the package root. It is not legal for this path to escape the package root, i.e., values like "../Foo", "/Foo" are invalid.

    opened by sdrpa 11
  • Feature/Swift 5.5 Supports

    Feature/Swift 5.5 Supports

    • Background In Swift 5.5, Swift team introduced Swift concurrency feature (https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html), which includes a new keyword await.

    This will cause confusion and conflict with freshOS/Then 's global function await.

    Users will get compiler errors if they build freshOS/Then with Swift 5.5. Although they still can use Then.await to avoid naming conflict, but it's not very convenient.

    • Changes
      • Rename await to awaitPromise.
        • Just FYI: Google's Promise also done this before: https://github.com/google/promises/commit/5d070816f183c8996626fe8ce066253feeb39723
      • Add one await function (which will call awaitPromise actually) with deprecated mark for short term back-port supports.

    All unit test cases passed on my machine, with Xcode 13 Beta 3.

    opened by RocZhang9673 10
  • whenAll.then issue in then execution

    whenAll.then issue in then execution

    Hello, I have an issue with the whenAll feature.

    The promises are executed successfully but the then block is never called. here is a code sample:

    return Promise { resolve, reject  in
        let asyncMatchers = matchers.filter { $0.asyncMatch }
        var promises = [Promise<MatchResult>]()
        for matcher in asyncMatchers {
            promises.append(matcher.matchTermsAsync(from: phraseComponents))
        } 
        Promises.whenAll(promises).then { results in
            resolve(results)
        }
    }
    

    As walk arround I managed to make it work like this:

    return Promise { resolve, reject  in
        let asyncMatchers = matchers.filter { $0.asyncMatch }
        async {
            for matcher in asyncMatchers {
                   let matchResults = try await(matcher.matchTermsAsync(from: phraseComponents))
                   results.append(contentsOf: matchResults)
             }
        }.then {
             resolve(results)
        }
    }
    

    Can you see any obvious reason why the .then block in never invoked? Does whenAll support concurrent execution for the promises?

    Thank you.

    opened by cliapis 10
  • Memory Leaks

    Memory Leaks

    I've found an instance where I can produce a memory leak when using a Promise from within a new Promise:

    class ViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
    
            noleaky().finally {
    
            }
    
            leaky().finally {
                
            }
        }
    
        func leaky() -> Promise<Void> {
            return Promise { [unowned self] resolve, reject in
                self.doSomething(for: 0)
                    .then(self.doSomething)
                    .onError(reject)
                    .finally(resolve)
            }
        }
    
        func noleaky() -> Promise<Void> {
            return doSomething(for: 0)
                .then(doSomething)
                .then(Promise.resolve())
        }
    
        func doSomething(for blah: Int) -> Promise<Int> {
            return Promise.resolve(3)
        }
    
    }
    

    I'm not sure if this is expected or not? Sample project is attached. ThenLeakExample.zip

    opened by ghost 10
  • Cloning the library takes a long while

    Cloning the library takes a long while

    Cloning the library takes a long while. Receiving objects downloads 359MB and Resolving deltas is very long.

    git clone https://github.com/freshOS/then.git
    Cloning into 'then'...
    remote: Counting objects: 897792, done.
    remote: Total 897792 (delta 0), reused 0 (delta 0), pack-reused 897792
    Receiving objects: 100% (897792/897792), `359.35 MiB` | 32.12 MiB/s, done.
    Resolving deltas:  59% (234493/391788)  
    
    opened by crarau 9
  • Synchronous promises don't chain properly

    Synchronous promises don't chain properly

    This is a corner case but could be interesting support it. When we have 3 promises encapsulating synchronous code, if we try to chain all of them, the chaining is not working properly.

    func promiseA() -> Promise<Int> { 
        return Promise { resolve, reject in 
            print("Promise A")
            resolve(result: 1) 
        } 
    }
    
    func promiseB() -> Promise<Int> {
        return Promise { resolve, reject in
            print("Promise B")
            resolve(result: 2)
        }
    }
    
    func promiseC() -> Promise<Int> {
        return Promise { resolve, reject in
            print("Promise C")
            resolve(result: 3)
        }
    }
    
    

    If we chain all of them, A and B are executed but C doesn't it:

    promiseA().then(promiseB()).then(promiseC())
    

    I think the problem in this case is in the method:

        public func then<X>(p:Promise<X>) -> Promise<X>{
            p.isFirstPromise = false
            successBlock = { t in p.start() }
            startPromiseIfNeeded()
            return p
        }
    

    When then is executed over promiseA, is defined the successBlock and startPromiseIfNeeded provokes promiseA starts, at the end of promiseA the successBlock is executed and promiseB starts although as all the code is synchronous promiseA and promiseB are executed in the same cycle while promiseB still doesn't have a successBlock assigned to execute promiseC. So when then is executed over promiseB, this is already started and fulfilled, startPromiseIfNeeded never calls start.

    I think a possible solution would be the next code although I'm not completely confident about it

        public func then<X>(p:Promise<X>) -> Promise<X>{
            p.isFirstPromise = false
            if state == .Fulfilled {
                p.start()
            } else {
                successBlock = { t in p.start() }
                startPromiseIfNeeded()
            }
            return p
        }
    

    Really this is a corner case, because per definition a Promise encapsulates some asynchronous operation and in this situation we are encapsulating synchronous operations, but could be interesting because some times we need to simulate the results of async operations inside Promises.

    opened by piterlouis 9
  • Recover

    Recover

    Enable to .recover() a failed Promise with a default value or another Promise

    Promise<Int>.reject()
      .recover(with: 12)
      .then { s in
        // s == 12
      }
    
    Promise<String>.reject()
      .recover { e in
        return Promise.resolve("NeverMind")
      }.then { s in
      // s == NeverMind
      }
    
    enhancement 
    opened by s4cha 8
  • Await is working ?

    Await is working ?

    Hi all,

    Like PromiseKit wrapping delegates example on CLLocationManager, I try to do the same with freshOS/then.

    https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#wrapping-delegate-systems

    The promise is working with:

    LocatorManager.shared.getLocation()
                .timeout(0.5)
                .then { location in
                    print("then" + String(data: try! JSONEncoder().encode(location), encoding: .utf8)!)
                }.onError { error in
                    print(error)
            }
    

    but not with await, my app is blocked. An idea..?

    Thank a lot for helping.

    do {
                let location = try await(LocatorManager.shared.getLocation().timeout(0.2))
                print("await" + String(data: try! JSONEncoder().encode(location), encoding: .utf8)!)
                
            } catch {
                print(error)
            }
    

    All LocatorManager singleton class code

    import Foundation
    import CoreLocation
    
    public class LocatorManager: NSObject, CLLocationManagerDelegate {
        static let shared = LocatorManager()
        private let locationManager = CLLocationManager()
        private var promise = Promise<DeviceLocation>()
        
        override private init() {
            super.init()
            locationManager.delegate = self
            locationManager.requestWhenInUseAuthorization()
        }
        
        func getLocation() -> Promise<DeviceLocation> {
            self.promise = Promise<DeviceLocation> { _, reject, _ in
                if !CLLocationManager.locationServicesEnabled() {
                    reject(LocatorError.locationServiceDisabled)
                    return
                }
                
                if CLLocationManager.authorizationStatus() == .authorizedWhenInUse ||
                    CLLocationManager.authorizationStatus() == .authorizedAlways {
                        self.locationManager.requestLocation()                
                } else {
                    reject(LocatorError.noUserAuthorization)
                }
            }
            
            return self.promise
        }
        
        public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
            let deviceLocation = DeviceLocation(latitude: locations.last!.coordinate.latitude,
                                                longitude: locations.last!.coordinate.longitude)
            self.promise.fulfill(deviceLocation)
        }
        
        public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
            self.promise.reject(error)
        }
    }
    
    public enum LocatorError: Error {
        case locationServiceDisabled
        case noUserAuthorization
    }
    
    opened by NasH5169 7
  • Swift 5 support

    Swift 5 support

    Sorry, I probably should've opened this issue before the PR. I'm mostly pasting the info from #51 here so that we can discuss these better. I've begun working on integrating the changes necessary to allow compilation of the project on Xcode 10.2 using its Swift 5 toolchain.

    All tests are currently passing, but I've had to relax the timings on some of them from 0.3 to 0.5 seconds in order to run successfully on simulators. Is that a problem?

    Some changes, such as the ones made to Promise will be breaking the current API, as the compiler can't (any longer?) disambiguate the types on its own. I'm open to ideas on these solutions!

    Thanks!

    opened by balecrim 6
  • M1 - Apple Silicon with arm64 excluded architecture issue

    M1 - Apple Silicon with arm64 excluded architecture issue

    Hi all

    In order to run my project in Apple Silicon I had to exclude the arch arm64. But I'm getting this error, could you add support to this please?

    Could not find module 'Then' for target 'x86_64-apple-ios-simulator'; found: arm64-apple-ios-simulator, at: /Users/.../.../Developer/Xcode/DerivedData/..../Build/Products/Debug-iphonesimulator/Then.swiftmodule

    To reproduce it:

    In M1 - Apple Silicon Mac:

    1. -Create new project
    2. Instal Swift package Then 6.0.0
    3. In build settings add excluded Architecture for Any iOS simulator SDK = arm64
    Screen Shot 2022-05-17 at 18 18 40 4. Import Then in ViewController.swift 5. Try to compile the project

    Screen Shot 2022-05-17 at 18 15 06

    opened by dasoga 2
  • Thread explosion causing a deadlock?

    Thread explosion causing a deadlock?

    Hi, we are using Then together with Vapor for a swift server side project. While doing some performance tests, we noticed that our current implementation is not able to support more than 64 concurrent requests without collapsing and being stuck in what it seems to be a deadlock.

    For each request, we are always creating an async block and inside we'll be calling multiple async methods with await.

    I've been able to reproduce the same issue using one of your tests. Our methods for each requests are similar to the testAsyncAwaitChainWorks one in AsyncAwaitTests class. If you modify that method to do the same code 100 times, it will look like this:

    func testAsyncAwaitChainWorks() {
      for _ in 0..<100 {
        let exp = expectation(description: "")
        print("starting new task")
        async {
          print("starting async block")
          let userId = try await(fetchUserId())
          XCTAssertEqual(userId, 1234)
          let userName = try await(fetchUserNameFromId(userId))
          XCTAssertEqual(userName, "John Smith")
          let isFollowed = try await(fetchUserFollowStatusFromName(userName))
          XCTAssertFalse(isFollowed)
          exp.fulfill()
          print("finishing async block")
        }
      }
      waitForExpectations(timeout: 1000.0, handler: nil)
    }
    

    Executing this will print 100 times starting new task, but it will only print 64 times starting async block and finishing async block will not be printed at all, the execution will never finished.

    Is there any way of avoiding this kind of issues and being able to use async/await approach while supporting a lot of concurrent requests?

    opened by roisg 1
  • Cocoapods support

    Cocoapods support

    Hi!

    I saw that Cocoapods is deprecated now, I would like to suggest adding support again since it doesn't change the way to develop for SPM as well.

    The library is a very useful library and Cocoapods still in use for a huge number of projects out there, would be nice to keep using this the way it is.

    Thanks.

    opened by bguidolim 3
  • Changes access level for

    Changes access level for "setProgress()" function

    This function is really useful but was set as internal. I propose set as public so everyone can use this.

    Use case:

    • You keep the promise in a property or array and need to update the progress via delegate (e.g.: URLSessionTaskDelegate)
    opened by bguidolim 0
  • Delete too early start before progress block registered

    Delete too early start before progress block registered

    Hi, I'm a stranger who started using this library. I appreciate your effort for this! This PR is related to issue #50 to let you guys know about what I found. I'm using tag 4.2.1. I tried deleting tryStartInitialPromiseAndStartIfneeded() from progress(block) function because otherwise progress(float) in Promise may be called before registering the block passed from the progress function. I hope this helps. Thanks for reading!

    opened by nator333 0
  • .progress should be able to return Progress, not Float

    .progress should be able to return Progress, not Float

    Currently, this library can only return Float value when using .progress directive. However, Alamofire and other network libraries return Progress instance instead of Float.

    I think thenPromise should be able to return Progress instance on .progress directive. For example:

    return Promise<URL> { resolve, reject, progress in
        Alamofire.download(escapedUrl, to: destination)
            .downloadProgress { downloadProgress in // downloadProgress is Progress class
                // let currentProgress = Float(downloadProgress.completedUnitCount) / Float(downloadProgress.totalUnitCount)
                // progress(currentProgress)
                progress(downloadProgress) // <- should be able to write like this
            }
            .response { response in
                ...
            }
    }
    
    opened by subdiox 1
Releases(7.0.0)
Owner
Fresh
Simple iOS tools to solve problems 99% of us have.
Fresh
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
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

Max Howell 14k Jan 5, 2023
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 260 Oct 12, 2022
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

Andreas Grosam 39 Jun 3, 2021
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
The easiest Future and Promises framework in Swift. No magic. No boilerplate.

Promis The easiest Future and Promises framework in Swift. No magic. No boilerplate. Overview While starting from the Objective-C implementation of Ju

Alberto De Bortoli 111 Dec 27, 2022
Promises is a modern framework that provides a synchronization construct for Swift and Objective-C.

Promises Promises is a modern framework that provides a synchronization construct for Objective-C and Swift to facilitate writing asynchronous code. I

Google 3.7k Dec 24, 2022
Lightweight promises for iOS, macOS, tvOS, watchOS, and Linux

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

David Ask 60 Aug 11, 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
Futures and Promises library

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

Victor Shamanov 17 Apr 5, 2019
A promises library written in Swift featuring combinators like map, flatMap, whenAll, whenAny.

Promissum is a promises library written in Swift. It features some known functions from Functional Programming like, map and flatMap. It has useful co

Tom Lokhorst 68 Aug 31, 2022
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

async_plus 132 Jan 6, 2023
A 99-player last-bird-flapping battle royale

Flappy Royale A HTML5 Game, built to be embedded in apps using Phaser 3. Setup Clone, and run yarn install, run yarn start to load up into the app: cd

Flappy Royale 149 Dec 17, 2022
COVID Safe Paths (based on Private Kit) is an open and privacy preserving system to use personal information to battle COVID

COVID Safe Paths is a mobile app for digital contract tracing (DCT) sponsored by Path Check a nonprofit and developed by a growing global community of engineers, designers, and contributors. Safe Paths is based on research originally conducted at the MIT Media Lab.

PathCheck Foundation 470 Nov 6, 2022
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

Daniele Margutti 2k Dec 24, 2022
⚡️ 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

Daniele Margutti 2k Dec 31, 2022
Hydra: Lightweight full-featured Promises, Async-Await Library in Swift

Async Functions for ECMAScript The introduction of Promises and Generators in EC

Ecma TC39 1.6k Oct 17, 2022
GCDTimer - Well tested Grand Central Dispatch (GCD) Timer in Swift

GCDTimer Well tested Grand Central Dispatch (GCD) Timer in Swift. Checkout the test file. Usage Long running timer import GCDTimer

Hemant Sapkota 183 Sep 9, 2022
PlayCover is a project that allows you to sideload iOS apps on macOS( currently arm, Intel support will be tested.

PlayCover Run iOS apps & games on M1 Mac with mouse, keyboard and controller support. Intel macs support will be implemented later. Installation Just

Alexandr 3.9k Jun 30, 2022
PlayCover is a project that allows you to sideload iOS apps on macOS( currently arm, Intel support will be tested.

PlayCover is a project that allows you to sideload iOS apps on macOS( currently arm, Intel support will be tested.

Alexandr 4k Jul 8, 2022