Giter Club home page Giter Club logo

brightfutures's Introduction

BrightFutures

⚠️ BrightFutures has reached end-of-life. After a long period of limited development activity, Swift's Async/Await has made the library obsolete. Please consider migrating from BrightFutures to async/await. When you do so, the async get() method will prove to be useful:

// in an async context...

let userFuture = User.logIn(username, password)
let user = try await userFuture.get()

// or simply:
let posts = try await Posts.fetchPosts(user).get()

The remainder of the README has not been updated recently, but is preserved for historic reasons.


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

BrightFutures implements proven functional concepts in Swift to provide a powerful alternative to completion blocks and support typesafe error handling in asynchronous code.

The goal of BrightFutures is to be the idiomatic Swift implementation of futures and promises. Our Big Hairy Audacious Goal (BHAG) is to be copy-pasted into the Swift standard library.

The stability of BrightFutures has been proven through extensive use in production. It is currently being used in several apps, with a combined total of almost 500k monthly active users. If you use BrightFutures in production, we'd love to hear about it!

Latest news

Join the chat at https://gitter.im/Thomvis/BrightFutures GitHub Workflow tests.yml status badge Carthage compatible CocoaPods version CocoaPods

BrightFutures 8.0 is now available! This update adds Swift 5 compatibility.

Installation

  1. Add the following to your Podfile:

    pod 'BrightFutures'
  2. Integrate your dependencies using frameworks: add use_frameworks! to your Podfile.

  3. Run pod install.

  1. Add the following to your Cartfile:

    github "Thomvis/BrightFutures"
    
  2. Run carthage update and follow the steps as described in Carthage's README.

Documentation

  • This README covers almost all features of BrightFutures
  • The tests contain (trivial) usage examples for every feature (97% test coverage)
  • The primary author, Thomas Visser, gave a talk at the April 2015 CocoaHeadsNL meetup
  • The Highstreet Watch App was an Open Source WatchKit app that made extensive use of an earlier version of BrightFutures

Examples

We write a lot of asynchronous code. Whether we're waiting for something to come in from the network or want to perform an expensive calculation off the main thread and then update the UI, we often do the 'fire and callback' dance. Here's a typical snippet of asynchronous code:

User.logIn(username, password) { user, error in
    if !error {
        Posts.fetchPosts(user, success: { posts in
            // do something with the user's posts
        }, failure: handleError)
    } else {
        handleError(error) // handeError is a custom function to handle errors
    }
}

Now let's see what BrightFutures can do for you:

User.logIn(username, password).flatMap { user in
    Posts.fetchPosts(user)
}.onSuccess { posts in
    // do something with the user's posts
}.onFailure { error in
    // either logging in or fetching posts failed
}

Both User.logIn and Posts.fetchPosts now immediately return a Future. A future can either fail with an error or succeed with a value, which can be anything from an Int to your custom struct, class or tuple. You can keep a future around and register for callbacks for when the future succeeds or fails at your convenience.

When the future returned from User.logIn fails, e.g. the username and password did not match, flatMap and onSuccess are skipped and onFailure is called with the error that occurred while logging in. If the login attempt succeeded, the resulting user object is passed to flatMap, which 'turns' the user into an array of his or her posts. If the posts could not be fetched, onSuccess is skipped and onFailure is called with the error that occurred when fetching the posts. If the posts could be fetched successfully, onSuccess is called with the user's posts.

This is just the tip of the proverbial iceberg. A lot more examples and techniques can be found in this readme, by browsing through the tests or by checking out the official companion framework FutureProofing.

Wrapping expressions

If you already have a function (or really any expression) that you just want to execute asynchronously and have a Future to represent its result, you can easily wrap it in an asyncValue block:

DispatchQueue.global().asyncValue {
    fibonacci(50)
}.onSuccess { num in
    // value is 12586269025
}

asyncValue is defined in an extension on GCD's DispatchQueue. While this is really short and simple, it is equally limited. In many cases, you will need a way to indicate that the task failed. To do this, instead of returning the value, you can return a Result. Results can indicate either a success or a failure:

enum ReadmeError: Error {
    case RequestFailed, TimeServiceError
}

let f = DispatchQueue.global().asyncResult { () -> Result<Date, ReadmeError> in
    if let now = serverTime() {
        return .success(now)
    }
    
    return .failure(ReadmeError.TimeServiceError)
}

f.onSuccess { value in
    // value will the NSDate from the server
}

The future block needs an explicit type because the Swift compiler is not able to deduce the type of multi-statement blocks.

Instead of wrapping existing expressions, it is often a better idea to use a Future as the return type of a method so all call sites can benefit. This is explained in the next section.

Providing Futures

Now let's assume the role of an API author who wants to use BrightFutures. A Future is designed to be read-only, except for the site where the Future is created. This is achieved via an initialiser on Future that takes a closure, the completion scope, in which you can complete the Future. The completion scope has one parameter that is also a closure which is invoked to set the value (or error) in the Future.

func asyncCalculation() -> Future<String, Never> {
    return Future { complete in
        DispatchQueue.global().async {
            // do a complicated task and then hand the result to the promise:
            complete(.success("forty-two"))
        }
    }
}

Never indicates that the Future cannot fail. This is guaranteed by the type system, since Never has no initializers. As an alternative to the completion scope, you could also create a Promise, which is the writeable equivalent of a Future, and store it somewhere for later use.

Callbacks

You can be informed of the result of a Future by registering callbacks: onComplete, onSuccess and onFailure. The order in which the callbacks are executed upon completion of the future is not guaranteed, but it is guaranteed that the callbacks are executed serially. It is not safe to add a new callback from within a callback of the same future.

Chaining callbacks

Using the andThen function on a Future, the order of callbacks can be explicitly defined. The closure passed to andThen is meant to perform side-effects and does not influence the result. andThen returns a new Future with the same result as this future that completes after the closure has been executed.

var answer = 10
    
let _ = Future<Int, Never>(value: 4).andThen { result in
    switch result {
    case .success(let val):
        answer *= val
    case .failure(_):
        break
    }
}.andThen { result in
    if case .success(_) = result {
        answer += 2
    }
}

// answer will be 42 (not 48)

Functional Composition

map

map returns a new Future that contains the error from this Future if this Future failed, or the return value from the given closure that was applied to the value of this Future.

fibonacciFuture(10).map { number -> String in
    if number > 5 {
        return "large"
    }
    return "small"
}.map { sizeString in
    sizeString == "large"
}.onSuccess { numberIsLarge in
    // numberIsLarge is true
}

flatMap

flatMap is used to map the result of a future to the value of a new Future.

fibonacciFuture(10).flatMap { number in
    fibonacciFuture(number)
}.onSuccess { largeNumber in
    // largeNumber is 139583862445
}

zip

let f = Future<Int, Never>(value: 1)
let f1 = Future<Int, Never>(value: 2)

f.zip(f1).onSuccess { a, b in
    // a is 1, b is 2
}

filter

Future<Int, Never>(value: 3)
    .filter { $0 > 5 }
    .onComplete { result in
        // failed with error NoSuchElementError
    }

Future<String, Never>(value: "Swift")
    .filter { $0.hasPrefix("Sw") }
    .onComplete { result in
        // succeeded with value "Swift"
    }

Recovering from errors

If a Future fails, use recover to offer a default or alternative value and continue the callback chain.

// imagine a request failed
Future<Int, ReadmeError>(error: .RequestFailed)
    .recover { _ in // provide an offline default
        return 5
    }.onSuccess { value in
        // value is 5 if the request failed or 10 if the request succeeded
    }

In addition to recover, recoverWith can be used to provide a Future that will provide the value to recover with.

Utility Functions

BrightFutures also comes with a number of utility functions that simplify working with multiple futures. These are implemented as free (i.e. global) functions to work around current limitations of Swift.

Fold

The built-in fold function allows you to turn a list of values into a single value by performing an operation on every element in the list that consumes it as it is added to the resulting value. A trivial usecase for fold would be to calculate the sum of a list of integers.

Folding a list of Futures is not very convenient with the built-in fold function, which is why BrightFutures provides one that works especially well for our use case. BrightFutures' fold turns a list of Futures into a single Future that contains the resulting value. This allows us to, for example, calculate the sum of the first 10 Future-wrapped elements of the fibonacci sequence:

let fibonacciSequence = [fibonacciFuture(1), fibonacciFuture(2),  ..., fibonacciFuture(10)]

// 1+1+2+3+5+8+13+21+34+55
fibonacciSequence.fold(0, f: { $0 + $1 }).onSuccess { sum in
    // sum is 143
}

Sequence

With sequence, you can turn a list of Futures into a single Future that contains a list of the results from those futures.

let fibonacciSequence = [fibonacciFuture(1), fibonacciFuture(2),  ..., fibonacciFuture(10)]
    
fibonacciSequence.sequence().onSuccess { fibNumbers in
    // fibNumbers is an array of Ints: [1, 1, 2, 3, etc.]
}

Traverse

traverse combines map and fold in one convenient function. traverse takes a list of values and a closure that takes a single value from that list and turns it into a Future. The result of traverse is a single Future containing an array of the values from the Futures returned by the given closure.

(1...10).traverse {
    i in fibonacciFuture(i)
}.onSuccess { fibNumbers in
    // fibNumbers is an array of Ints: [1, 1, 2, 3, etc.]
}

Delay

delay returns a new Future that will complete after waiting for the given interval with the result of the previous Future. To simplify working with DispatchTime and DispatchTimeInterval, we recommend to use this extension.

Future<Int, Never>(value: 3).delay(2.seconds).andThen { result in
    // execute after two additional seconds
}

Default Threading Model

BrightFutures tries its best to provide a simple and sensible default threading model. In theory, all threads are created equally and BrightFutures shouldn't care about which thread it is on. In practice however, the main thread is more equal than others, because it has a special place in our hearts and because you'll often want to be on it to do UI updates.

A lot of the methods on Future accept an optional execution context and a block, e.g. onSuccess, map, recover and many more. The block is executed (when the future is completed) in the given execution context, which in practice is a GCD queue. When the context is not explicitly provided, the following rules will be followed to determine the execution context that is used:

  • if the method is called from the main thread, the block is executed on the main queue
  • if the method is not called from the main thread, the block is executed on a global queue

If you want to have custom threading behavior, skip do do not the section. next 😉

Custom execution contexts

The default threading behavior can be overridden by providing explicit execution contexts. You can choose from any of the built-in contexts or easily create your own. Default contexts include: any dispatch queue, any NSOperationQueue and the ImmediateExecutionContext for when you don't want to switch threads/queues.

let f = Future<Int, Never> { complete in
    DispatchQueue.global().async {
        complete(.success(fibonacci(10)))
    }
}

f.onComplete(DispatchQueue.main.context) { value in
    // update the UI, we're on the main thread
}

Even though the future is completed from the global queue, the completion closure will be called on the main queue.

Invalidation tokens

An invalidation token can be used to invalidate a callback, preventing it from being executed upon completion of the future. This is particularly useful in cases where the context in which a callback is executed changes often and quickly, e.g. in reusable views such as table views and collection view cells. An example of the latter:

class MyCell : UICollectionViewCell {
    var token = InvalidationToken()
    
    public override func prepareForReuse() {
        super.prepareForReuse()
        token.invalidate()
        token = InvalidationToken()
    }
    
    public func setModel(model: Model) {
        ImageLoader.loadImage(model.image).onSuccess(token.validContext) { [weak self] UIImage in
            self?.imageView.image = UIImage
        }
    }
}

By invalidating the token on every reuse, we prevent that the image of the previous model is set after the next model has been set.

Invalidation tokens do not cancel the task that the future represents. That is a different problem. With invalidation tokens, the result is merely ignored. Invalidating a token after the original future completed does nothing.

If you are looking for a way to cancel a running task, you could look into using NSProgress.

Credits

BrightFutures' primary author is Thomas Visser. He is lead iOS Engineer at Highstreet. We welcome any feedback and pull requests. Get your name on this list!

BrightFutures was inspired by Facebook's BFTasks, the Promises & Futures implementation in Scala and Max Howell's PromiseKit.

License

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

brightfutures's People

Contributors

andrewsb avatar bkase avatar chunkerchunker avatar danj-stripe avatar e-marchand avatar gitter-badger avatar jeroenb-triple avatar jgh- avatar kennytm avatar kimdv avatar mathebox avatar mattfenwick avatar mickeyreiss avatar noobish1reviewer avatar nvh avatar paiv avatar peyton avatar phimage avatar possen avatar robertoaceves avatar romanpodymov avatar sajjon avatar slessans avatar thomvis avatar tmu avatar tonyarnold avatar wiruzx avatar wvteijlingen avatar yonaskolb avatar zenkimoto avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

brightfutures's Issues

"when" promise?

Is there a way to run a set of promises simultaneously with this library?

Bring consistency between `error` and `failure`.

The completion callbacks and Result use failure, while Promise uses the word error. Is there any reason for this? It seems to me it would be more clear that one leads to another if they were both called either error or failure.

callbackExecutionQueue results in EXC_BAD_ACCESS

I'm trying to perform a background operation using BrightFutures. The result (MKPolyline) should be displayed in a MKMapView; thus the callback should be performed on the main thread. However the callbackExecutionQueue results in an extra dispatch queue on top of the main queue. This results in EXC_BAD_ACCESS when the MKPolyline is added to the MapView. When the highlighted line is removed, everything works as expected.

Simplified usage:

        future(context: Queue.global) { () -> Result<MKPolyline> in
            return Result(polyline)
        }
            .onSuccess(context: Queue.main) { polyline in
                self.mapView.addOverlay(polyline)
            }

best way to implement retry

while there are flatMap to chain futures for successful results, what is the best way to do retry kind of logic when there is failure? i.e. how do we chain futures for failure path?

Closure passed to flatMap needs return type specified?

Maybe I'm missing something here, but it seems like I must specify the return type of the closure passed to flatMap. When I use just the parameters, it fails to build with error: "Missing argument for parameter 'f' in call".

Doesn't work:

Artist.findOne("1").flatMap { resource, meta in
    return resource.findRelated("albums")
}

Does work:

Artist.findOne("1").flatMap { resource, meta -> Future<([Resource], Meta?)> in
    return resource.findRelated("albums")
}

Map function does not work on Futures

Applying the map function on a future seems to never finish. The map block never gets executed. I tried debugging the map myself, even looking at the BrightFutures code by setting breakpoints there, but that has only caused xcode to seg fault (a lovely feature I keep seeing with swift). So I am stuck.

Here is the source of me using the map, the 'dummyAbbrDictF' function is the culprit.

func dummyStationsF() -> Future<[Station]> {
    return future {
      let expected12StSt = Station(name: "12th St. Oakland City Center", abbreviation: "12th", city: "Oakland", latitude: 37.803664, longitude: -122.271604)
      let expected16StSt = Station(name: "16th St. Mission", abbreviation: "16th", city: "San Francisco", latitude: 37.765062, longitude: -122.419694)
      let expectedMontSt = Station(name: "Montgomery St.", abbreviation: "mont", city: "San Francisco", latitude: 37.789256, longitude: -122.401407)
      let expectedRockSt = Station(name: "Rockridge", abbreviation: "rock", city: "Oakland", latitude: 37.844601, longitude: -122.251793)
      let expectedSFOSt = Station(name: "San Francisco Int'l Airport", abbreviation: "sfia", city: "San Francisco Int'l Airport", latitude: 37.616035, longitude: -122.392612)
      let expectedSSFSt = Station(name: "South San Francisco", abbreviation: "ssan", city: "South San Francisco", latitude: 37.664174, longitude: -122.444116)

      let stns = [expected12StSt,expected16StSt,expectedMontSt,expectedRockSt,expectedSFOSt,expectedSSFSt]
      return Result.Success(Box(stns))
    }
  }

  func dummyAbbrDictF() -> Future<Dictionary<String, Station>> {
    return self.dummyStationsF().map { stations -> Dictionary<String, Station> in
      var dict: Dictionary<String, Station> = [:]
      for station in stations {
        dict[station.abbreviation] = station
      }
      return dict
    }
  }

I also put all the code into a branch https://github.com/firemuzzy/BartApp, the 'testAbbrs' test in StationStoreTests is failing due the issue of the map function getting stuck.

Pod build fails - unknown option character `X' in: -Xlinker

I installed the pod using:

pod 'BrightFutures', :git => "https://github.com/Thomvis/BrightFutures.git"

when building the project I get:

(null): /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/libtool: unknown option character `X' in: -Xlinker

I'm using Xcode 6.1.1 (6A2008a)
For the time being I just embedded BrightFutures in my project, without cocoapods :-/

Anyone?

full error:

Libtool /Users/yelled3/Library/Developer/Xcode/DerivedData/Smore-bikwhdybmwziuicildyctmstzgwd/Build/Products/Debug-iphonesimulator/libPods-Smore-BrightFutures.a normal i386
    cd /Users/yelled3/Development/smore-ios/Smore/Pods
    export IPHONEOS_DEPLOYMENT_TARGET=8.0
    export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin"
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/libtool -static -arch_only i386 -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator8.1.sdk -L/Users/yelled3/Library/Developer/Xcode/DerivedData/Smore-bikwhdybmwziuicildyctmstzgwd/Build/Products/Debug-iphonesimulator -filelist /Users/yelled3/Library/Developer/Xcode/DerivedData/Smore-bikwhdybmwziuicildyctmstzgwd/Build/Intermediates/Pods.build/Debug-iphonesimulator/Pods-Smore-BrightFutures.build/Objects-normal/i386/Pods-Smore-BrightFutures.LinkFileList -L/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphonesimulator -Xlinker -add_ast_path -Xlinker /Users/yelled3/Library/Developer/Xcode/DerivedData/Smore-bikwhdybmwziuicildyctmstzgwd/Build/Intermediates/Pods.build/Debug-iphonesimulator/Pods-Smore-BrightFutures.build/Objects-normal/i386/Pods_Smore_BrightFutures.swiftmodule -framework Foundation -o /Users/yelled3/Library/Developer/Xcode/DerivedData/Smore-bikwhdybmwziuicildyctmstzgwd/Build/Products/Debug-iphonesimulator/libPods-Smore-BrightFutures.a

error: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/libtool: unknown option character `X' in: -Xlinker
Usage: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/libtool -static [-] file [...] [-filelist listfile[,dirname]] [-arch_only arch] [-sacLT] [-no_warning_for_no_symbols]
Usage: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/libtool -dynamic [-] file [...] [-filelist listfile[,dirname]] [-arch_only arch] [-o output] [-install_name name] [-compatibility_version #] [-current_version #] [-seg1addr 0x#] [-segs_read_only_addr 0x#] [-segs_read_write_addr 0x#] [-seg_addr_table <filename>] [-seg_addr_table_filename <file_system_path>] [-all_load] [-noall_load]

function signature specialization?

I'm having trouble deciphoering an error, and it's listed in my code (rather than a BrightFutures file) so probably it's my own bug, but in case it's meaningful I wanted to post here (and perhaps you'll have seen something like this before?)

function signature specialization <Arg[0] = Owned To Guaranteed, Arg[1] = Owned To Guaranteed, Arg[2] = Owned To Guaranteed> of MyPackage.MyFile.myFunction (MyFile.myFunction)(ObjectiveC.NSMutableDictionary, then : () -> ()) -> () (MyFile.swift:1562)

Dependency 'Result', '0.6-beta.1' causing newer frameworks to break

Your podspec includes:

  s.dependency 'Result', '0.6-beta.1'

Unfortunately this is causing newer frameworks (such as ReactiveCocoa, Swift 2 branch), Moya, and others to break:

dyld: Library not loaded: @rpath/Result.framework/Result
  Referenced from: /Users/zbeckman/Library/Developer/CoreSimulator/Devices/FCFAAFBF-1AB4-4223-9BCC-CA91CBDE96DC/data/Containers/Bundle/Application/9E7272D7-5C66-48F9-9E37-892E41662655/Glimpulse.app/Frameworks/ReactiveCocoa.framework/ReactiveCocoa
  Reason: Incompatible library version: ReactiveCocoa requires version 1.0.0 or later, but Result provides version 0.6.0
(lldb) 

Can't pass Tuple to Promise#success

Trying to pass a tuple to Promise#success and getting hit with a compiler warning stating

Missing argument for parameter #2 in call

Code to re-produce the error:

var promise = Promise<(Int, String)>()
promise.success( (1, "hello") )

I'm sure its a problem with the compiler. Tried in Xcode 6.0.1 and 6.1. Thought I would let you know though.

Conditional Future chaining

Hi,

Just start using BrightFutures and it's really fun! Thanks for the work done :)

I have a question, recently hit this patter two times:

future {
    //Do some computation
    return Result(value: successValue)
}.flatMap { goodValue in
    if goodValue.holdSomeCondition() {
        return doSomethingElse()
    } else {
        return Future(value: goodValue)
    }
}

The question is, how can I avoid the flatMap when the condition isn't true? I found filter but if the condition is false the operation fail. I could do the filter and handle the error but I don't like it much.

Thanks

Doesn't work with xCode plugin template

Cocoapods doesn't work at all.

Carthage works but I can't add it to my project properly - it simply doesn't have "General tab" for binaries and etc. And so I can't simply include your library.

Could you advice another solution?

How to pass a Promise to a method

I have the following:

func promiseProvider() -> Future<User> {
  let promise = Promise<User>()

  promiseHandler(promise)

  return promise.future
}

func promiseHandler<T>(aPromise: T) {
  // do something with he promise:
  promise.success(User())
}

I'm basically trying to pass the promise I created in PromiseProvider to another method (promiseHandler). But for whatever reason, Swift complains:
Cannot invoke 'promiseHandler' with an argument list of type '(Promise<User>)'

Any help would be appreciated, thanks!

ENABLE_BITCODE

Just updated to v3.0.0-beta.1 and Carthage builds, but emits the following error:

ld: warning: -weak_framework is treated as -framework when used with -bitcode_bundle (Xcode setting ENABLE_BITCODE=YES). Weak linking may still happen based on availability mark-up in headers

exploring persistent future as notification alternative

Hello,

We would like to have a more functional notification mechanism due to the following problems in current IOS Notification system:

  • the signature is loose (userInfo), not able to take advantage of swift's powerful compile type checking
  • not possible to declare composition like flatMap
  • only classes can post notifications right now

Both notification and futures let user register future events and handlers, so at least conceptually it seems like a natural extension for futures to handle such use case. The main obstacle for the current BrightFutures implementation is the assumption that future is only executed once, any subsequent completion/callback attempts are treated as illegal state. If this behavior can be configured or allow subclass to override, we should be able to use this persistent future to model the notification use case...

thoughts, concerns or gotcha?

Use of unresolved identifier 'FutureUtils'

var allFutures = [future1, future2]
FutureUtils.sequence(allFutures).onSuccess { result in
    println("complete")
}

When attempting to use FutureUtils class like above, I get the following error:

Use of unresolved identifier 'FutureUtils'

Looks like the FutureUtils functions were turned into free functions here: 17d4f21. But when I change my code to a "free function":

var allFutures = [future1, future2]
sequence(allFutures).onSuccess { result in
    println("complete")
}

I get this error:

Cannot invoke 'onSuccess' with an argument list of type '((_) -> _)'

Any ideas of how to use the FutureUtils functions? Also, the documentation needs to be updated regarding the FutureUtils methods.

symbol(s) not found for architecture x86_64

Hey there, I'm using BrightFutures as a dependency in Spine and I'm getting the following when compiling:

Ld /Users/kurko/Library/Developer/Xcode/DerivedData/Dinero-frfqupqfiefqgtevsufnmfrmbtcl/Build/Products/Debug-iphonesimulator/Spine.framework/Spine normal x86_64
    cd /Users/kurko/www/ios/Dinero/Pods
    export IPHONEOS_DEPLOYMENT_TARGET=8.0
    export PATH="/Applications/Xcode7.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin:/Applications/Xcode7.app/Contents/Developer/usr/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
    /Applications/Xcode7.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -arch x86_64 -dynamiclib -isysroot /Applications/Xcode7.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator9.0.sdk -L/Users/kurko/Library/Developer/Xcode/DerivedData/Dinero-frfqupqfiefqgtevsufnmfrmbtcl/Build/Products/Debug-iphonesimulator -F/Users/kurko/Library/Developer/Xcode/DerivedData/Dinero-frfqupqfiefqgtevsufnmfrmbtcl/Build/Products/Debug-iphonesimulator -filelist /Users/kurko/Library/Developer/Xcode/DerivedData/Dinero-frfqupqfiefqgtevsufnmfrmbtcl/Build/Intermediates/Pods.build/Debug-iphonesimulator/Spine.build/Objects-normal/x86_64/Spine.LinkFileList -install_name @rpath/Spine.framework/Spine -Xlinker -rpath -Xlinker @executable_path/Frameworks -Xlinker -rpath -Xlinker @loader_path/Frameworks -mios-simulator-version-min=8.0 -Xlinker -objc_abi_version -Xlinker 2 -fobjc-arc -fobjc-link-runtime -L/Applications/Xcode7.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphonesimulator -Xlinker -add_ast_path -Xlinker /Users/kurko/Library/Developer/Xcode/DerivedData/Dinero-frfqupqfiefqgtevsufnmfrmbtcl/Build/Intermediates/Pods.build/Debug-iphonesimulator/Spine.build/Objects-normal/x86_64/Spine.swiftmodule -framework BrightFutures -framework Foundation -framework SwiftyJSON -single_module -current_version 1 -Xlinker -dependency_info -Xlinker /Users/kurko/Library/Developer/Xcode/DerivedData/Dinero-frfqupqfiefqgtevsufnmfrmbtcl/Build/Intermediates/Pods.build/Debug-iphonesimulator/Spine.build/Objects-normal/x86_64/Spine_dependency_info.dat -o /Users/kurko/Library/Developer/Xcode/DerivedData/Dinero-frfqupqfiefqgtevsufnmfrmbtcl/Build/Products/Debug-iphonesimulator/Spine.framework/Spine

Undefined symbols for architecture x86_64:
  "direct generic type metadata pattern for Result.Result", referenced from:
      ext.Spine.BrightFutures.Future<A, B where B: Swift.ErrorType>.onServerFailure <A, B where B: Swift.ErrorType> (BrightFutures.Future<A, B>)((B) -> ()) -> BrightFutures.Future<A, B> in FutureExtensions.o
      ext.Spine.BrightFutures.Future<A, B where B: Swift.ErrorType>.onNetworkFailure <A, B where B: Swift.ErrorType> (BrightFutures.Future<A, B>)((B) -> ()) -> BrightFutures.Future<A, B> in FutureExtensions.o
      ext.Spine.BrightFutures.Future<A, B where B: Swift.ErrorType>.onClientFailure <A, B where B: Swift.ErrorType> (BrightFutures.Future<A, B>)((B) -> ()) -> BrightFutures.Future<A, B> in FutureExtensions.o
  "protocol witness table for <A, B where B: Swift.ErrorType> Result.Result<A, B> : Result.ResultType in Result", referenced from:
      ext.Spine.BrightFutures.Future<A, B where B: Swift.ErrorType>.onServerFailure <A, B where B: Swift.ErrorType> (BrightFutures.Future<A, B>)((B) -> ()) -> BrightFutures.Future<A, B> in FutureExtensions.o
      ext.Spine.BrightFutures.Future<A, B where B: Swift.ErrorType>.onNetworkFailure <A, B where B: Swift.ErrorType> (BrightFutures.Future<A, B>)((B) -> ()) -> BrightFutures.Future<A, B> in FutureExtensions.o
      ext.Spine.BrightFutures.Future<A, B where B: Swift.ErrorType>.onClientFailure <A, B where B: Swift.ErrorType> (BrightFutures.Future<A, B>)((B) -> ()) -> BrightFutures.Future<A, B> in FutureExtensions.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I saw other issues here with the same problem and it seems like setting Result explicitly in the Podfile would solve it, but it doesn't for me. Here's my Podfile.lock:

PODS:
  - Alamofire (3.0.1)
  - BrightFutures (3.0.0):
    - Result (= 0.6.0-beta.4)
  - Realm (0.96.1):
    - Realm/Headers (= 0.96.1)
  - Realm/Headers (0.96.1)
  - RealmSwift (0.96.1):
    - Realm (= 0.96.1)
  - Result (0.6.0-beta.4)
  - Spine (0.2):
    - BrightFutures (~> 3.0)
    - SwiftyJSON (~> 2.3.0)
  - SwiftyJSON (2.3.0)

DEPENDENCIES:
  - Alamofire (~> 3.0)
  - RealmSwift
  - Spine (from `https://github.com/kurko/Spine.git`, tag `swift-2.0`)

EXTERNAL SOURCES:
  Spine:
    :git: https://github.com/kurko/Spine.git
    :tag: swift-2.0

CHECKOUT OPTIONS:
  Spine:
    :git: https://github.com/kurko/Spine.git
    :tag: swift-2.0

SPEC CHECKSUMS:
  Alamofire: 2457e1b2e6c46bb05c3a598c542b7bfd08893775
  BrightFutures: 4a3f46747e5633562bdcef55c9e291bfb7288c34
  Realm: d05e4621f67fb1c36acd4e573ac5b52a407fc2cc
  RealmSwift: 6c96a72b385027d9593b69f6821c697fe843eb68
  Result: f7927152cbaec0c043abb06ba0bc432678818418
  Spine: f3414766a0e500dc150b838af6663288890495ce
  SwiftyJSON: 8d6b61a70277ef2a5d710d372e06e7e2d87fb9e4

COCOAPODS: 0.39.0

Any idea what it could be? Seems like it's either BrightFutures or Result.

Support Carthage

It would be nice to have Carthage support. Since Carthage doesn't support semver with extra labels added, we need an intermediate release for that. Also, the scheme must be marked as shared.

Master doesn't compile on XCode 6.1.1 / Swift 1.1 due to default context parameters

I get following 4 errors when compiling the latest master (6e4ca98) on XCode 6.1.1 / Swift 1.1

/Users/teemu/all/flancer/protos/BrightFutures/BrightFutures/FutureUtils.swift:63:33: error: missing argument for parameter 'f' in call
            return zero.flatMap { zeroVal in

/Users/teemu/all/flancer/protos/BrightFutures/BrightFutures/Future.swift:261:24: error: missing argument for parameter 'callback' in call
        self.onFailure { err in
                       ^
/Users/teemu/all/flancer/protos/BrightFutures/BrightFutures/Future.swift:434:29: error: missing argument for parameter 'f' in call
        return self.flatMap { thisVal -> Future<(T,U)> in
                            ^
/Users/teemu/all/flancer/protos/BrightFutures/BrightFutures/Future.swift:442:29: error: missing argument for parameter 'f' in call
        return self.flatMap { value -> Result<T> in

ERROR ITMS-90060: "This bundle is invalid.

Hello.

When using BrightFutures 3.0.0 I'm unable to upload the .ipa to iTunes Connect. It looks like its because of a dependency to Result<0.6.0-beta.4> which makes the transporter freak out.

[15:39:57]: [Transporter Error Output]: ERROR ITMS-90060: "This bundle is invalid. The value for key CFBundleShortVersionString '0.6.0-beta.4' in the Info.plist file must be a period-separated list of at most three non-negative integers."

Is it possible to use a released version instead? :)

BrightFutures 5

Now BrightFutures 3 has been released, it's time to think about what BrightFutures 4 could/should look like. I'd like to gather and track feedback from the community and my own idea's in this issue.

Candidate features:

  • Make it possible to use BrightFutures with just the standard library (possibly needed when Swift becomes Open Source without Foundation)

Any other ideas?

Update: I've renamed the issue from "BrightFutures 4" to "BrightFutures 5", because of the near release of 4 that did not have the aforementioned goals.

Improve future composition

Hi,
I'm having a lot of issues handling the error type when composing futures.

Typically:

let three: Future<Int, NoError> = future(3)
let fut: Future<Int, NSError> = future { //long computation that can fail }
let composed =fut.flatMap { _ in three)

Produces an compile error. But it all works when both futures have the same Error Type.

I expected composed to be of type Future<Int, NSError> because one of the error type is a Void type, only the other one makes sense.
If both types conform to ErrorType then I would expect a generic ErrorType on which I can patten match or a composed error (like a tuple of optional errors).

Is there a way of achieving this behaviour? Is it intended? Am I doing something wrong?

Complete a future multiple times?

So far I'm seeing this is not possible. I'm looking for a way to replace callback logic where the callback is executed 2 times.

The reason I have this is a webservice call with a local-database cache. I always return to the caller the result of the local database and do webservice call in the background. If the result of the webservice call is different from the result which I returned from the local db, I return this result (again) to the caller. So my callback can be executed 1 or 2 times. The idea is to show the cached result to the client as soon as possible, and if there's a change in the remote data to refresh the UI immediately with it.

Previously I had (roughly) something like:

func myCall(handler: (MyResult) -> ()) {
    getFromLocalDB {localDBResult in
         handler(localDBResult)
    }

    getFromRemote {remoteResult
         //...
         if remoteResult != localDBResult {
             handler(remoteResult)
         }
    }

This is what I came up as replacement using futures (I have little experience with futures, maybe there's a better way):

func myCall() -> Future<MyResult, NSError> {

    let promise = Promise<MyResult, NSError>()

    getFromLocalDB.onComplete {result in
         // ...
         promise.success(localDBResult)
    }

    getFromRemote.onComplete {result
         // ...
         if remoteResult != localDBResult {
             promise.success(remoteResult)
         }

    return promise.future
}

But this causes an assertion error on the second success call because the future is already completed.

I came up with a solution in which I return 2 futures, like this:

func myCall() -> (cached: Future<MyResult, NSError>, remote:  Future<MyResult, NSError>) {

    let promiseCached = Promise<MyResult, NSError>()
    let promiseRemote = Promise<MyResult, NSError>()

    getFromLocalDB.onComplete {result in
         // ...
         promiseCached.success(localDBResult)
    }

    getFromRemote.onComplete {result
         // ...
         if remoteResult != localDBResult {
             promiseRemote.success(remoteResult)
         }

    return (cached: promiseCached.future, remote: promiseRemote.future)
}

And handle with a common closure:

let completion: Result<[MyResult], NSError> -> () = {result in
    // ...  
}

let (cached: Future<[MyResult], NSError>, remote: Future<[MyResult], NSError>) = getMyResults()

cached.onComplete { // this additional closure is necessary - somehow it was not possible to pass the completion closure directly 
    completion($0)
}
remote.onComplete {
    completion($0)
}

And that works, but I wonder if there's a better way. Having the handler callback called twice is maybe not the cleanest way to implement this, but there may be other scenarios with a callback-similar setup which has to be called multiple times.

Use robrix/Box instead of own Box type

When using BrightFutures together with other libraries that implement a Box to work around the enum limitations, it can be confusing to have multiple Box types in your codebase. I would suggest using robrix/Box, since that seems to become the standard micro-framework to use for solving this problem.

Playgrounds

Hi @Thomvis

I created a playground and a pull request. Let me know if that's something you'd be interested in. More info in the pull request itself.

BrightFutures needs a 1.0 release

This issue is created to track the progress towards releasing a 1.0 version. Please chime in if you have any feedback or requests!

TODO:

  • Reconsider the default callback thread. Callbacks are currently scheduled on a background thread (dispatch global queue) unless an execution context is explicitly given. Maybe it is better to make the main thread the default callback thread. This makes a lot of sense in UIKit apps.
  • I don't like that most methods are defined twice: once with an execution context and once without an execution context. I'd think that it is possible to use optional parameters.
  • The naming of Future.succeeded(..) and f.succeeded() (same goes for failed) is confusing.
  • I'm not sure if the switch-case shorthand provided through f.completed({}, {})(and in a lesser way in f.succeeded { } and f.failed { } adds enough value to justify its existence.
  • Both Future and TaskResult have succeeded, failed and completed (/handle) methods, which seems duplicate.
  • I don't like the use of TaskResultValueWrapper. Luckily, it is almost never exposed through public API.
  • Not all FutureUtils functions from Scala have been ported over yet
  • Decide whether Future should be a pure functional future (two states: pending or complete), more like a 'task' (pending, complete, cancelled, succeeded, failed) or somewhere in between
  • Wrap up 1.0 documentation

Postponed until after 1.0:

  • The methods in FutureUtils should work on any SequenceType, not only on Array
  • Move helper classes (Queue, Semaphore, ExecutionContext) to a separate framework
  • A logo?
  • Add Objective-C support

Possible to send multiple values over time to a Future?

This is probably straying a bit into RAC's territory, but is there a way to send multiple values over time to a Future? My use case is the CLLocationManager's delegate API, where things like authorisation can return indeterminate states before returning a proper value.

I'd prefer not to have to use some kind of "retry" mechanism, but I get this might not be the point of BF.

Add new Future extension 'chain'

In my project find useful to chain multiple futures:

  let f = service.create(user, password: user.password).chain { user in
            return self.service.login(user.email, user.password) 
  }

Both the service request returns a Future type.

I created this extension that might be useful to others:

extension Future {
    func chain<U> (f: (T)-> Future<U> ) -> Future<U> {
        let p = Promise<U>()
        self.onComplete { result in
            switch result{
            case .Success(let boxedFuture):
                let future = f(boxedFuture.value)
                future.onSuccess { value in
                    p.success(value)
                }
                future.onFailure{ err in
                    p.failure(err)
                }
                break
            case .Failure(let e):
                p.failure(e)
                break
            }
        }
        return p.future
    }
}

Push newer versions to CocoaPods

I could be wrong, but it looks like you have newer versions that are not in CocoaPods.

# pod search BrightFutures

-> BrightFutures (1.0.0-beta.3)
   A simple Futures & Promises library for iOS and OS X written in Swift
   pod 'BrightFutures', '~> 1.0.0-beta.3'
   - Homepage: https://github.com/Thomvis/BrightFutures
   - Source:   https://github.com/Thomvis/BrightFutures.git
   - Versions: 1.0.0-beta.3, 1.0.0-beta.2 [master repo]

Adding dispatch_after to Queue

Hi @Thomvis,

In my current project, I need to use the dispatch_after() GCD function. I do like your Queue wrapper very much and I think it would be a great fit. Let me know if something like this would fit the philosophy behind this project. I've added a pull request for your review.

Add "isCompleted" property to Future

From the Scala docs:

"Returns whether the future has already been completed with a value or an exception."

This would be really helpful to have.

I can't install BrightFutures in my project

I wanted to add BrightFutures to my OSX project using cocoapods. But for some reason it won't let me install it.

My Podfile

platform :osx, '10.8'

use_frameworks!

pod 'BrightFutures', '~> 2.0.1'
$ pod install --verbose
  Preparing

Updating local specs repositories

Updating spec repo `master`
  $ /usr/local/bin/git pull --ff-only
  Already up-to-date.

Analyzing dependencies

Inspecting targets to integrate
  Using `ARCHS` setting to build architectures of target `Pods`: (``)

Resolving dependencies of `Podfile`
[!] Unable to satisfy the following requirements:

- `BrightFutures (~> 2.0.1)` required by `Podfile`

/usr/local/var/rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/cocoapods-0.38.1/lib/cocoapods/resolver.rb:388:in `handle_resolver_error'
/usr/local/var/rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/cocoapods-0.38.1/lib/cocoapods/resolver.rb:69:in `rescue in resolve'
/usr/local/var/rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/cocoapods-0.38.1/lib/cocoapods/resolver.rb:56:in `resolve'
/usr/local/var/rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/cocoapods-0.38.1/lib/cocoapods/installer/analyzer.rb:535:in `block in resolve_dependencies'
/usr/local/var/rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/cocoapods-0.38.1/lib/cocoapods/user_interface.rb:59:in `section'
/usr/local/var/rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/cocoapods-0.38.1/lib/cocoapods/installer/analyzer.rb:533:in `resolve_dependencies'
/usr/local/var/rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/cocoapods-0.38.1/lib/cocoapods/installer/analyzer.rb:70:in `analyze'
/usr/local/var/rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/cocoapods-0.38.1/lib/cocoapods/installer.rb:209:in `analyze'
/usr/local/var/rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/cocoapods-0.38.1/lib/cocoapods/installer.rb:131:in `block in resolve_dependencies'
/usr/local/var/rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/cocoapods-0.38.1/lib/cocoapods/user_interface.rb:59:in `section'
/usr/local/var/rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/cocoapods-0.38.1/lib/cocoapods/installer.rb:130:in `resolve_dependencies'
/usr/local/var/rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/cocoapods-0.38.1/lib/cocoapods/installer.rb:103:in `install!'
/usr/local/var/rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/cocoapods-0.38.1/lib/cocoapods/command/project.rb:71:in `run_install_with_update'
/usr/local/var/rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/cocoapods-0.38.1/lib/cocoapods/command/project.rb:101:in `run'
/usr/local/var/rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/claide-0.9.1/lib/claide/command.rb:312:in `run'
/usr/local/var/rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/cocoapods-0.38.1/lib/cocoapods/command.rb:48:in `run'
/usr/local/var/rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/cocoapods-0.38.1/bin/pod:44:in `<top (required)>'
/usr/local/var/rbenv/versions/2.1.2/bin/pod:23:in `load'
/usr/local/var/rbenv/versions/2.1.2/bin/pod:23:in `<main>'

Safe to naïvely use 'self' methods?

Probably best explained with a code example. I have a UIViewController which fetches data wrapped in BrightFutures, and on completion fills in that data in its views - simple enough:

class SomeViewController: UIViewController {

    override func viewDidAppear() {
        super.viewDidAppear()
        // loadUser returns a Future
        User.loadUser().onSuccess(callback: displayUser)
    }

    func displayUser(user: User) {
        println("displaying user \(user)")
    }
}

What happens if SomeViewController was deallocated before the loadUser future resolves? Does BrightFutures retain the callback method and the relevant object associated with it, or do I need to always wrap it in a 'weak self' wrapper like so:

    User.loadUser().onSuccess { [weak self] user in
      self?.displayUser(user)
   }

I much prefer the first form - and preferably an even simpler one (currently the callback argument name is required). It's already a huge improvement over the existing success/failure block or delegate method approaches used, IMO.

(note: Swift 1.2, BrightFutures 2.x)

Cannot build using Swift 2.0 xcode 7

I keep seeing this compile crashes:

    Undefined symbols for architecture x86_64:
      "direct generic type metadata pattern for Result.Result", referenced from:
          type metadata accessor for Result.Result<Swift.String, BrightFutures.NoError> in Track.o
      "protocol witness table for <A, B where B: Swift.ErrorType> Result.Result<A, B> : Result.ResultType in Result", referenced from:
          static UpshotCore.Track.(demographicData in _355BED5052E6A82D51CFE2CBBD95410A) (UpshotCore.Track.Type)(Swift.String) -> () in Track.o
    ld: symbol(s) not found for architecture x86_64
    clang: error: linker command failed with exit code 1 (use -v to see invocation)

Even when I'm just using test code:

func doStuff() -> Future<String, NoError> {
    let promise = Promise<String, NoError>()

    promise.success("hi")

    return promise.future
}

How to cancel future?

Say I have a long-running background calculation, that the user should be able to cancel. The current implementation does not include some communication mechanism for signalling this event. How could such a feature be implemented? I think the following concerns should be addressed:

  • Signal the background calculation of the cancellation; it would be up to the future to check for this flag and stop further processing.
  • Signal the promise of some "cancel" or "failure" state; so that any subsequent callbacks also recognize the cancellation of the promise/future.

ErrorType does not conform ErrorType :/

I'll throw this one out there, no clue what's going on.

let foo: Future<String, ErrorType>

error: protocol type ErrorType does not conform to protocol ErrorType because ErrorType is not declared @objc

// Custom protocols don't work either
protocol MyError: ErrorType {}

// and trying to make it @objc backfires
@objc public protocol MyObjCError: ErrorType {}

error: @objc protocol MyObjCError cannot refine non-@objc protocol ErrorType

Any wizard has a magic spell to fix this one?

// this is ugly
enum FutureError : ErrorType {
    case Error(ErrorType)
}
// and this is not kosher
class FutureError : ErrorType { }

Swift 2.0
XCode Version 7.0 beta 5 (7A176x)

Is it possible to use 1.0 with Swift 2 until 2.0 is ready?

I'm currently updating my app for Swift 2.0, but can't get my app to run with any of the Swift 2.0 branches using Carthage.

I was able to (after some hacking) get the "swiftier-2.0" branch to build with Carthage, but I kept getting a fatal error whenever I would call onSuccess

New workaround needed for the "non-fixed multi-payload" bug

In Xcode Beta 6, compiling BrightFutures gives the following error:

'__conversion' functions are no longer allowed

It looks like there is not another way to do an implicit type conversion to workaround the .Success value in the TaskResult enum. Unfortunately, writing the code as it should be written:

public enum TaskResult<T> {
  case Success(T)
  case Failure(NSError)
  ...
}

still results in an "unimplemented IR generation feature non-fixed multi-payload enum" error.

I think it is necessary to expose the unboxing of the generic value to users of the API, but I didn't want to make a pull-request in case you had a good solution.

Promises Chaining

Hello @Thomvis! Great project! I love the simplicity and how the code works. However, I didn't notice anything in the documentation that would allow for promises chaining like they have in PromiseKit. I also looked at the code as well. The andThen() function only supports side effects and not the same idea as chaining promises and I didn't see anything else in the code that would support that functionality.

Is there something I missed or is promises chaining not part of the design of BrightFutures? Or maybe it's something that's part of the roadmap? Thanks.

Concurrent queues

It looks like Queue only exposes a mechanism for creating queues that are DISPATCH_QUEUE_SERIAL.

Is there a reason you decided not to include a way to create a concurrent (DISPATCH_QUEUE_CONCURRENT) queue directly?

Compile error: `direct generic type metadata pattern for Result.Result`

I use BrightFutures with Alamofire

I write extension for Alamofire.Request.

request.responseJSONPromise() run with no error.

However, when I chain Future using flatMap, Xcode7 shows a compile error message.

screen shot 2558-09-19 at 15 54 40

Here's my code

public func getToken(username username: String, password: String) -> Future<String, DRCloudServerError> {

    let request = Alamofire.request( .POST, url, parameters: params)

    return request.responseJSONPromise().flatMap{ json -> Future<String, DRCloudServerError> in

        let promise = Promise<String, DRCloudServerError>()
        if let accessToken = json["access_token"].string{
            promise.trySuccess(accessToken)
        }else{
            promise.tryFailure(DRCloudServerError.JSONParseError)
        }
        return promise.future
    }
}

and my Alamofire.Request extension

extension Alamofire.Request{

    func responseJSONPromise() -> Future<JSON,DRCloudServerError>{

        let promise = Promise<JSON, DRCloudServerError>()
        responseJSON{ request, response, result in

            if let error = self.handleStatusCodeError(response, data: result.data){
                promise.tryFailure(error)
            }

            switch(result){
            case let .Failure(_, error):
                let errorDescription = "\(error)"
                promise.tryFailure(DRCloudServerError.RequestError(errorDescription))
            case let .Success(value):
                let json = JSON(value)
                promise.trySuccess(json)
            }
        }
        return promise.future
    }
}

objetive-c support in protocols using BrightFutures

I'm on version 1.0.1

I have a protocol for some couchbase lite utilities

import Foundation
import BrightFutures

@objc protocol CouchUtilsProtocol {
    var manager: CBLManager { get }

    func indexUpdate(
        databaseName: String,
        designDocName: String,
        viewName: String
    ) -> Future<Bool>

}

I have to add @objc because I use dependency injection ('Typhoon', '3.1.7') on compilation I get:

Method cannot be marked @objc because its result type cannot be represented in objective-c

Is this a bug with BrightFutures Objective-C support not added fully etc (#12 confuses me a bit) or simply because Objective-c do not have generics ?

And/Or do I simply declare the protocol wrongly ~ a here and now work around are very welcome!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.