Giter Site home page Giter Site logo

promisekit's Introduction

PromiseKit

badge-pod badge-languages badge-pms badge-platforms codecov


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 clearer, more readable code. Your co-workers will thank you.

UIApplication.shared.isNetworkActivityIndicatorVisible = true

let fetchImage = URLSession.shared.dataTask(.promise, with: url).compactMap{ UIImage(data: $0.data) }
let fetchLocation = CLLocationManager.requestLocation().lastValue

firstly {
    when(fulfilled: fetchImage, fetchLocation)
}.done { image, location in
    self.imageView.image = image
    self.label.text = "\(location)"
}.ensure {
    UIApplication.shared.isNetworkActivityIndicatorVisible = false
}.catch { error in
    self.show(UIAlertController(for: error), sender: self)
}

PromiseKit is a thoughtful and complete implementation of promises for any platform that has a swiftc. It has excellent Objective-C bridging and delightful specializations for iOS, macOS, tvOS and watchOS. It is a top-100 pod used in many of the most popular apps in the world.

codecov

Quick Start

In your Podfile:

use_frameworks!

target "Change Me!" do
  pod "PromiseKit", "~> 8"
end

PromiseKit 8 supports recent Xcodes (13+). Some Podspecs were dropped as a result. Pull requests are welcome.

PromiseKit 6, 5 and 4 support Xcode 8.3, 9.x and 10.0; Swift 3.1, 3.2, 3.3, 3.4, 4.0, 4.1, 4.2, 4.3 and 5.0 (development snapshots); iOS, macOS, tvOS, watchOS, Linux and Android; CocoaPods, Carthage and SwiftPM; (CI Matrix).

For Carthage, SwiftPM, Accio, etc., or for instructions when using older Swifts or Xcodes, see our Installation Guide. We recommend Carthage or Accio.

PromiseKit and Swift 5.5+ Async/Await

As of Swift 5.5, the Swift language now offers support for built-in concurrency with async / await. See Async+ for a port of PromiseKit's most useful patterns to this new paradigm.

Professionally Supported PromiseKit is Now Available

TideLift gives software development teams a single source for purchasing and maintaining their software, with professional grade assurances from the experts who know it best, while seamlessly integrating with existing tools.

Get Professional Support for PromiseKit with TideLift.

Documentation

Extensions

Promises are only as useful as the asynchronous tasks they represent. Thus, we have converted (almost) all of Apple’s APIs to promises. The default CocoaPod provides Promises and the extensions for Foundation and UIKit. The other extensions are available by specifying additional subspecs in your Podfile, e.g.:

pod "PromiseKit/MapKit"          # MKDirections().calculate().then { /*…*/ }
pod "PromiseKit/CoreLocation"    # CLLocationManager.requestLocation().then { /*…*/ }

All our extensions are separate repositories at the PromiseKit organization.

I don't want the extensions!

Then don’t have them:

pod "PromiseKit/CorePromise", "~> 8"

Note: Carthage installations come with no extensions by default.

Networking

Promise chains commonly start with a network operation. Thus, we offer extensions for URLSession:

// pod 'PromiseKit/Foundation'  # https://github.com/PromiseKit/Foundation

firstly {
    URLSession.shared.dataTask(.promise, with: try makeUrlRequest()).validate()
    // ^^ we provide `.validate()` so that eg. 404s get converted to errors
}.map {
    try JSONDecoder().decode(Foo.self, with: $0.data)
}.done { foo in
    //…
}.catch { error in
    //…
}

func makeUrlRequest() throws -> URLRequest {
    var rq = URLRequest(url: url)
    rq.httpMethod = "POST"
    rq.addValue("application/json", forHTTPHeaderField: "Content-Type")
    rq.addValue("application/json", forHTTPHeaderField: "Accept")
    rq.httpBody = try JSONEncoder().encode(obj)
    return rq
}

Support for Alamofire is welcome, please submit a PR.

Support

Please check our Troubleshooting Guide, and if after that you still have a question, ask at our Gitter chat channel or on our bug tracker.

Security & Vulnerability Reporting or Disclosure

https://tidelift.com/security

promisekit's People

Contributors

abizern avatar allen-zeng avatar cecuba avatar codecaffeine avatar dependabot[bot] avatar drekka avatar dtaylor1701 avatar feighter09 avatar filipzawada avatar garthsnyder avatar iammxrn avatar javilorbada avatar jeehut avatar josejulio avatar kdubb avatar lammertw avatar ldiqual avatar linusu avatar lutzifer avatar mxcl avatar nathanhosselton avatar nathanli avatar neallester avatar pgherveou avatar romanpodymov avatar ryanschneider avatar shergin avatar tgaul avatar tikitu avatar zlangley 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  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

promisekit's Issues

Calling Finally only (no then) can silently fail

PromiseKit.m line 249:

    [handlers addObject:^(id passthru){

handlers can end up nil, and then fails to addObject

My use case (no 'then', first app load after install):

[SomePromise]
.catch(^(NSError *error) { NSLog(@"error: %@",error); } )
.finally(^() { //show screen no matter what } );

have also commented out catch and can repeat, usually 1/2 tries.

Convenience Method for running Promises on Main Thread

The default dispatch_promise runs the promise on a background thread. There is dispatch_promise_on, which would allow me to select the main queue, but I feel that a convenience method for this makes the most sense, something like dispatch_promise_on_main or dispatch_promis_main.

Why? Because another great use of promises is to make UI Animations easy to work with. For example, in my game I have the following code:

    /// Did Opponent Guy Faint?
    if ([self isOpponentDead]) {
        // make him faint
        [self opponentFaintWithCallback:^{
            NSString *text = [NSString stringWithFormat:@"%@ fainted!", self.currentOpponent.name];
            [self showText:text untilDismissed:^{
                NSInteger expGain = [self.currentOpponent experienceGainedWhenDefeated];
                NSDictionary *levelInfo = [self.currentPlayer gainExperience:expGain];

                /// if you grew a level OR if you need to delete a move, do all that.

                NSString *exp = [NSString stringWithFormat:@"%@ got %d exp points!", self.currentPlayerGuy.name, expGain];
                [self showText:exp untilDismissed:^{
                    if (levelInfo != nil) {
                        NSString *lvlUp = [NSString stringWithFormat:@"%@ grew to level %d!", self.currentPlayerGuy.name,
                                           self.currentPlayerGuy.level.integerValue];
                        [self updateLabels:NO];
                        [self showText:lvlUp untilDismissed:^{
                            if ([levelInfo[@"moves"] count] > 0) {
                                Move *m = [levelInfo[@"moves"] lastObject];
                                NSString *learned = [NSString stringWithFormat:@"%@ learned %@!", self.currentPlayerGuy.name, m.name];
                                [self showText:learned untilDismissed:^{

                                    if (self.currentPlayerGuy.moves.count > 10) {
                                        DLog(@"... we... need to delete a move.");
                                        [self opponentSendOutNextEnemyWithCallback:^(BOOL didSendOut) {
                                            if (!didSendOut) {
                                                [self opponentLosingSequence];
                                            } else {
                                                [self emptyMoveQueue];
                                                [self showMainMenu];
                                            }
                                        }];
                                    } else {
                                        [self opponentSendOutNextWithCallback:^(BOOL didSendOut) {
                                            if (!didSendOut) {
                                                [self opponentLosingSequence];
                                            } else {
                                                [self emptyMoveQueue];
                                                [self showMainMenu];
                                            }
                                        }];
                                    }
                                }];
                            } else {
                                [self opponentSendOutNextWithCallback:^(BOOL didSendOut) {
                                    if (!didSendOut) {
                                        [self opponentLosingSequence];
                                    } else {
                                        [self emptyMoveQueue];
                                        [self showMainMenu];
                                    }
                                }];
                            }
                        }];
                    } else {
                        [self opponentSendOutNextWithCallback:^(BOOL didSendOut) {
                            if (!didSendOut) {
                                [self opponentLosingSequence];
                            } else {
                                [self emptyMoveQueue];
                                [self showMainMenu];
                            }
                        }];
                    }
                }];
            }];
        }];
        return;
    }

Oh my god, it's hideous. Promises will make this sequence much, much better, but all the animations should run on the main thread - the promise implementation is just a wonderful way to handle the asynchronous nature of the entire thing. Showing text, waiting for them to dismiss it, etc.

If you think this is a good idea, I can go ahead and add a pull request.

Parse+PromiseKit

Hey all,

Sorry if is the wrong place to post this, but I wanted to share that I am working on a Parse+PromiseKit category.

While it's functional and nearly complete, I am not sure if I'm handling the Parse methods that return a "succeeded" boolean in the best way. I figured passing the boolean as the result was pointless because promises can already tell us if a task succeeded for failed. That means if the boolean is passed as the result, thens would always receive YES.

Instead, I figured it would be much more helpful to have the object the task succeeded on. For example [object promiseSave] would then object (or self) so that you could read the object/data that was just saved. Similarly, [PFObject promiseSaveAll:] thens the array of objects that were saved, rather than the redundant YES boolean. But if I do this for some methods, should I do it for all (then the object(s) the task operated on)? It seems inconsistent just to do it for some. Thoughts, anyone?

Feel free to contribute. Once the documentation is completed and README revised, I'll see if this can be added to the Parse podspec. Otherwise I'll submit as a separate pod.

Thanks!

PromiseKit w/ Objective-C++

Hey, just checked out the library - wanted to say thanks for the awesome work! Enjoyed learning about it. :) Didn't see an issue about this here, so I thought you might want to know... PromiseKit can't be imported into any file which includes Objective-C++. Try creating a .mm file and importing it. It's due to PromiseKit's usage of @import syntax rather than #import, which objective-c++ may not support right now.

Support a progress handler

Some Promise libraries support the addition of a progress handler. It is meant for communicating progress in long-running operations (some more discussion here).

What are your thoughts on implementing this handler in PromiseKit? I have a specific use case that I've seen other libraries deal with using progress handlers:

  • I have a network request (that returns a Promise) that may or may not hit a shared NSURLCache. In case it does, and if the user explicitly sets it (via a new NSURLRequestCachePolicy), the request serves the cached data and then fetches from the network anyway. This is so that the user gets potentially stale data immediately while fresh data is being fetched in the background.

I would possibly like to use progress to support this use case, serving the cached data through this handler and then the final, fresh, data through the normal fulfillment handler.

Other than using progress, do you see any potential way of supporting this behaviour using just the two currently supported handlers?

Thank you!

Not returning from catch doesn't continue with next catch handler

One thing I noticed when using PromiseKit is that if you want to have a catch that doesn't continue on to a chained then after it runs, you must explicitly return the NSError at the end of the catch block. Lack of a return statement acts the same as return nil;.

Another promise system I used (a hand-built one we had at a former company — it was written in Lua, but semantically similar) would re-propagate an error automatically if there was no return in the catch block.

Having looked at how this is implemented in PromiseKit, it seems like this could be supported by adding a distinction between PMKNull and another value, let's call it PMKNoReturn, internally and handling them differently in catch.

Canceling a promise

Consider an asynchronous HTTP GET method request that returns a promise:

- (Promise*)GET:(NSString*)path;

Users of this API may want to cancel the underlying asynchronous work, the HTTP request. Currently PromiseKit does not allow cancelable promises.

I've been looking into javascript promises and cancelation. There is some discussion in the A+ spec https://github.com/promises-aplus/cancellation-spec/issues

Any thoughts on cancelation?

Leaks in +[PMKPromise new]

I'm using PromiseKit for an app I'm currently developing. By looking for leaks inside the app from Instruments, I realized that every time I call [PMKPromise new] I get a leak.

The following is the function call that leaks (PMKPromise:385):

this->_promiseQueue = PMKCreatePromiseQueue();

Any idea of why that is?

PromiseKit+Parse

Has anyone started working on a Parse implementation? If so I'm interested in contributing, please let me know.

Unwrap PMKManifold

Is it possible to unwrap a variable that has been wrapped through PMKManifold?

I'm doing something like this:

-(void)cachedList:(Class)providerClass deleteOrphanedObjects:(BOOL)deleteOrphanedObjects success:(void (^)(NSArray *result))success failure:(void (^)(NSString *error))failure {
    ALTCachedRequest *gr = [[ALTCachedRequest alloc] init];
    gr.cacheKey = @"";
    ALTBaseProvider *provider = [[providerClass alloc] initWithDatabaseController:_database andRequestOperationManager:_manager andBaseURL:WSBaseURL];
    provider.deleteOrphanedObjects = deleteOrphanedObjects;
    provider.request = gr;
    [provider fetchData].then(^(NSArray *cachedData, PMKPromise *freshData) {
        success(cachedData);
        return freshData;
    }).then(^(NSArray *freshData) {
        success(freshData);
    }).catch(^(NSError *error) {
        failure([error localizedDescription]);
    });
}

and the provider fetchData method is this one:

- (PMKPromise *)fetchData {
    return dispatch_promise(^{
        id fresh = [self downloadDataFromWS];
        id cached = [self fetchObjectsFromDb];

        return PMKManifold(cached, fresh);
    });
}

and fetchObjectsFromDb is this one:

- (PMKPromise *)fetchObjectsFromDb {
    return [PMKPromise new:^(PMKPromiseFulfiller fulfiller, PMKPromiseRejecter rejecter) {
        [self.database fetchLabels].then(^(id res) {
            fulfiller(res);
        });
    }];
}

The cachedData object being returned, which should be an NSArray * is actually a PMKPromise *. From the documentation I take it should actually be the type I set in the arguments of then, but it looks like I'm missing something.
Thanks!

Promises/A+

Hi, I was wondering if this implementation complies (or intends to) with the Promises/A+ specification http://promisesaplus.com/

I think this is something worth clarifying too.

Looks like really nice work by the way, good luck.

P.S
I'm also wondering why you chose to use the 'old' promise construction style (Deferred) rather than the new one (Promise constructor), thanks.

Crash when returning primitive types

dispatch_promise(^{
    return 3;
}).then(^(NSInteger i) {
    //
});

This crashes here since the return type is obviously not id.
I don't know what's the cleanest way to solve this. It's kind of acceptable that primitive types don't work. Maybe even leave it as-is and update the docs, or start supporting it?

(I caught this when accidentally returning "string" instead of @"string". 🙈)

Reconsider catching exceptions

Why does PromiseKit propagate exceptions (instances of the class NSException) to clients? Wouldn't it be more appropriate to follow the Cocoa convention of using errors (instances of the class NSError) exclusively to indicate failure?

Since clients rescue from erros instead of catch exceptions, I suggest to rename -[Promise catch] to -[Promise rescue] or -[Promise error]. If clients write code that throws exceptions, it is the clients fault that should be handled during development. Error handling at runtime should not occur by catching exceptions.

The Error Handling Programming Guide - A Note on Errors and Exceptions states the following:

It is important to keep in mind the difference between error objects and exception objects in Cocoa and Cocoa Touch, and when to use one or the other in your code. They serve different purposes and should not be confused.

Exceptions (represented by NSException objects) are for programming errors, such as an array index that is out of bounds or an invalid method argument. User-level errors (represented by NSError objects) are for runtime errors, such as when a file cannot be found or a string in a certain encoding cannot be read. Conditions giving rise to exceptions are due to programming errors; you should deal with these errors before you ship a product. Runtime errors can always occur, and you should communicate these (via NSError objects) to the user in as much detail as is required.

Although exceptions should ideally be taken care of before deployment, a shipped application can still experience exceptions as a result of some truly exceptional situation such as “out of memory” or “boot volume not available.” It is best to allow the highest level of the application—the global application object itself—to deal with these situations."

Let me know what you think.

PromiseKit.org example deprecation

Wrapping e.g. Parse

- (Promise *)allUsers {
    return [Promise new:^(PromiseResolver fulfiller, PromiseResolver rejecter){
        PFQuery *query = [PFQuery queryWithClassName:@"User"];
        [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
            if (!error) {
                fulfiller(objects);
            } else {
                rejecter(error);
            }
        }];
    }];
}

I believe this should be
return [Promise new:^(PromiseFulfiller fulfiller, PromiseRejecter rejecter) {
now that PromiseResolver is deprecated

Is it possible to use PromiseKit to handle header-only response?

First of all, thank you for the great library! Using it is like a breeze!

I'm using PromiseKit in an IOS app to communicate with a Rails RESTful backend,
and there're some calls that return only header, say by executing head 200 on the backend.

What I've tried is:

I used [NSURLConnection POST:url formURLEncodedParameters:parameters] to post data to backend, the backend did receive the data and responded with a head 200 message, but PromiseKit is reporting the following exception:

2014-07-31 11:19:39.501 HelloPOS[13223:60b] Error
Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be
completed. (Cocoa error 3840.)" (No value.) UserInfo=0xa4c5a90
{NSDebugDescription=No value., PMKURLErrorFailingDataKey=<CFData
0xa4c19d0 [0x1f60ec8]>{length = 1, capacity = 16, bytes = 0x20},
PMKURLErrorFailingURLResponseKey=<NSHTTPURLResponse: 0xa3a8170> { URL:
http://SOME_APP.SOME_HOST.com/api/v1/sales.json } { status code: 200,
headers {
"Cache-Control" = "max-age=0, private, must-revalidate";
Connection = "keep-alive";
"Content-Length" = 1;
"Content-Type" = "application/json";
Date = "Thu, 31 Jul 2014 03:19:25 GMT";
Etag = ""7215ee9c7d9dc229d2921a40e899ec5f"";
Server = "WEBrick/1.3.1 (Ruby/2.0.0/2014-05-08)";
Via = "1.1 vegur";
"X-Content-Type-Options" = nosniff;
"X-Frame-Options" = SAMEORIGIN;
"X-Request-Id" = "5a1c1aa1-4b42-41ea-8397-a5df8fa956bc";
"X-Runtime" = "0.013359";
"X-Xss-Protection" = "1; mode=block"; } }}

It's probably because header-only response has no body, and the error is caused

PS: I've changed the URL in the exception message

So the problem is: Is it possible to handle header-only response with PromiseKit?

Fulfilling a promise with no arguments

It is currently required to pass an argument to the fulfiller callback, as per the callback's signature:

typedef void (^PMKPromiseFulfiller)(id);

However, I find that I often don't need to pass any arguments at all. Any chance we can accommodate this use case somehow?

Then block does not execute if it returns nil

If you return nil from a then block, the block never gets executed.

myPromise.then(^{
    // do something - this will never execute
   return nil;
});

Put this works just fine.

myPromise.then(^{
    // do something - this will execute
});

The docs do mention

Not returning from a catch handler (or returning nil) causes PromiseKit to consider the Promise complete. No further bubbling occurs.

which is similar, but in our case the then block still executes if the return type is void; it just doesn't like nil.

If this is by design, it should be mentioned in the docs. I've been converting a project from Bolts and my continue (then) blocks often return nil as the block signature (BFContinuationBlock) has a return type of id.

[[self saveAsync:obj] continueWithBlock:^id(BFTask *task) {
  if (task.isCancelled) {
    // the save was cancelled.
  } else if (task.error) {
    // the save failed.
  } else {
    // the object was saved successfully.
    PFObject *object = task.result;
  }
  return nil; // Bolts requires you to return something.
              // Doing this in PromiseKit will cause the block to never execute.
}];

Property 'then' not found on object of type 'id'

This error came when updating PromiseKit to version 0.9.14.2. Seems like the PromiseKit+When.h-Header isn´t imported anywhere in the build process.

When going back to 0.9.13.2, it works again.

improved handling of void blocks

turning void blocks into [NSNull null], would make promises that fulfill or reject to nothing, work well when used with promise aggregators like all

Supporting void blocks would be nice, especially for methods that aggregate promises.

Access Promise state?

How can I check whether a given Promise is in Pending or Resolved state? I'm looking at http://blog.popularpays.com/tech/2014/4/28/popular-promises and those guys are accessing a property called 'pending' on a Promise, but it seems it does not exist in the HEAD version.

- (Promise *)poll {
    if (self.poller.pending)
        return self.poller;

I can see a private macro defined in PromiseKit.m, but nothing else that I can use publicly.

#define IsPending(o) (((Promise *)o)->result == nil)

Unneeded(?) Logging during error rejection

In + (Promise *)new:(void(^)(PromiseResolver, PromiseResolver))block:

    id rejecter = ^(id error){
        if (promise->result)
            @throw PMKE(@"Promise already fulfilled/rejected");
        if ([error isKindOfClass:[Promise class]])
            @throw PMKE(@"You may not reject a Promise");
        if (!error)
            error = [NSError errorWithDomain:PMKErrorDomain code:PMKErrorCodeUnknown userInfo:nil];
        if (![error isKindOfClass:[NSError class]])
            error = NSErrorWithThrown(error);

        NSLog(@"PromiseKit: %@", error);  // we refuse to let errors die silently

        promise->result = error;
        RejectRecursively(promise);
    };

Why is the NSLog statement there. The comment implies that we don't want to have errors silently lost via promises. But in this case the error hasn't had a chance to be caught and properly handled.

Perhaps I'm misreading this code, but shouldn't we wait for RejectRecrusively to execute before declaring the error silently missed?

SIGSEGV objc_msgSend() selector name: addObject:

We are regularly getting exceptions like the below reported from testers' devices. We're doing a pretty normal call with dispatch_promise.then.then... with nothing that should be too weird. A few steps return regular objects and a few return promises.

I'm not sure what else to include that might be useful. We're using

pod 'PromiseKit/base', '~>0.9.8'
pod 'PromiseKit/Foundation', '~>0.9.8'

Thanks for the help & promisekit looks great--I'll clean up and contribute back wrappers for some libraries we use soon.

Exception Type:  SIGSEGV
Exception Codes: SEGV_ACCERR at 0x818caf1c
Crashed Thread:  0

Application Specific Information:
objc_msgSend() selector name: addObject:

Thread 0 Crashed:
0   libobjc.A.dylib                      0x3b154626 objc_msgSend + 6
1   Treasure                             0x0023ccc9 __20-[PMKPromise thenOn]_block_invoke26 (PromiseKit.m:176)
2   Treasure                             0x0023bb6b __18-[PMKPromise then]_block_invoke (PromiseKit.m:150)
3   Treasure                             0x0002fc2b -[TrAppDelegate applicationWillEnterForeground:] (TrAppDelegate.m:311)
4   UIKit                                0x3334ec77 -[UIApplication _sendWillEnterForegroundCallbacks] + 92
5   UIKit                                0x332f41b5 -[UIApplication _handleApplicationResumeEvent:] + 1146
6   UIKit                                0x330f3085 -[UIApplication handleEvent:withNewEvent:] + 1870
7   UIKit                                0x330f2871 -[UIApplication sendEvent:] + 70
8   UIKit                                0x33156cc9 _UIApplicationHandleEvent + 614
9   GraphicsServices                     0x3575faed _PurpleEventCallback + 606
10  GraphicsServices                     0x3575f6d7 PurpleEventCallback + 32
11  CoreFoundation                       0x3089cab7 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 32
12  CoreFoundation                       0x3089ca53 __CFRunLoopDoSource1 + 344
13  CoreFoundation                       0x3089b227 __CFRunLoopRun + 1396
14  CoreFoundation                       0x30805f0f CFRunLoopRunSpecific + 520
15  CoreFoundation                       0x30805cf3 CFRunLoopRunInMode + 104
16  GraphicsServices                     0x3575e663 GSEventRunModal + 136
17  UIKit                                0x3315116d UIApplicationMain + 1134
18  Treasure                             0x000690cb main (main.m:31)
19  libdyld.dylib                        0x3b657ab7 start + 0

Add Class Prefix

Please change Promise and Deferred classes to PKPromise and PKDeferred or similar to minimize the chance of class name conflicts.

Ensure Thread Safety

The main case is thread safety for the same pending promise when thening from multiple threads.

Otherwise a thorough review should be done to check everything else is safe. Mostly due to our use of GCD and the immutability of resolved promises, we're good.

Bring Swift feature set up to Objc parity

Hey, I'm using the Swift variant of PromiseKit and playing around. I would really enjoy having a richer API namely:

  • Promise<T>.resolve(x:T) -> Promise<T> which creates a resolved promise with the value x. Today this is done with Promise(x) which I believe is more ambiguous.
  • Promise<T>.reject(e:NSError) -> Promise<T> which creates a rejected promise with the reason e
  • Promise.all(a:Promise<A>,b:Promise<B>,c:Promise<C>) -> Promise<(A,B,C)> and similarly overloads for 1 to 8 arguments, creates a promise that fulfills when they all fulfill or rejects when one rejects similarly to JavaScript's Promise.all, since Swift has no macros and arrays are homogenically typed this is a lot of repetition but very useful.
  • Promise<T>.all(arr:[Promise<T>]) -> Promise<[T]> - takes an array of promises and returns a single promise that fulfills when they all fulfill and rejects when either one rejects. This is the "variable length" version of all from above but it requires all promises to have the same type.
  • Promise<[T]>.map(fn:(T) -> Promise<U>) -> Promise<U> - maps an array of promises each to a promise and resolves when they all resolve, similar to returning a map of the array in the .then.

Other than that - great work! Coming from JS PromiseKit is really nice to work with.

Build error for version above 0.9.14

  • Installed with CocoaPods
  • Version 0.9.14 works (my previous version before updating with pod update)
  • Version 0.9.15.2 (current latest) fails with build error (see below)
  • Building for both Mac and iOS support

image

The afflicting file: https://github.com/mxcl/PromiseKit/blob/3b0e805340c27c208a3a0f8825fa07858e651e21/objc/UIActionSheet%2BPromiseKit.h

#import <PromiseKit/fwd.h>
#import <UIKit/UIActionSheet.h>

@interface UIActionSheet (PromiseKit)
/**
 Thens the dismissedButtonIndex and the actionSheet itself as the second
 parameter. This promise will never be rejected.
 */
- (PMKPromise *)promiseInView:(UIView *)view;
@end

I am showing the result of version 0.9.15.2 above.

Tested Versions:

  • FAIL - 0.9.15.2
  • FAIL 0.9.15.1
  • FAIL 0.9.15
  • PASS 0.9.14

Unused function NSMethodSignatureForBlock

short of adding:

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
static NSMethodSignature *NSMethodSignatureForBlock(id block) {
...
}
#pragma GCC diagnostic pop

to the file, anyway to get this function to play nice with -Wunused-function ? Driving me crazy.

Ability to recognise which promise failed while using aggregate methods

In older API if we use methods like when that take multiple promises, we would get NSArray of sub-errors + results, right now we only get a single NSError. Now I like cleaner distinction between error and results but right now there is no way to know which promise failed, which complicates things when you care or can recover from some errors.

One can wrap each promise into a catch statement before using aggregate method but it might be much more complicated code in many cases.

It would be useful to be able to query which promise failed, what do you think?

Loose interpretation of promise/a+

My Objective-C isn't fantastic, so I might be totally wrong about this.

But I have some promise experience

Anyways, a quick skim leaves me concerned:

Given: a settled promise, https://github.com/mxcl/PromiseKit/blob/master/PromiseKit.m#L137 appears to synchronously invoke the callback of a chained then.

This would be a serious departure from the spec.

The callbacks must not be invoked until the execution context stack is once again fresh.
see: https://github.com/promises-aplus/promises-spec#the-then-method specifically point 4. Without this, we unleash Zalgo onto our code base.

How can I use PromiseKit with delegate based HTTP requests?

For example I have 3 HTTP requests executing in turn.
Because we are using ASI-HTTP (unfortunately), how can I make use of PromiseKit to implement it?
I have seen the AFNetworking example, which return new Promise object with block, but ASI is delegate based, I have no idea how to implement the new Promise interface.

Thanks.

Promise<Void> catch->Void is ambiguous

I thought that swift incorrectly define type of a closure, but casting doesn't help.
Example from README.md doesn't work for me as well.
Is it bug, or i'm doing something wrong?

screenshot

let promise: Promise<Int> = Promise({ success, failure in
    success(10)
})

promise.then { number in
    self.remainingTimeLabel.text = "int is \(number)"
}.catch { error in
    self.remainingTimeLabel.text = "erorr is \(error)"
}

Nice way to run on specific queues

One of the things that Bolts does pretty well is let you choose a queue to run a block on, while this is manual with PromiseKit. I think it'd be useful to be able to write:

dispatch_queue_t queue = ...;
dispatch_promise(queue, ^{
    return [self makeValueAsync];
}).then(dispatch_get_main_queue(), ^(id value) {
    [self.view displayValue:value];
    return [self logDisplayInfo:self.view];
}).then(^{
    // No queue specified: synchronously run on the same queue immediately after
    // logDisplayInfo's promise is resolved
});

Also Bolts has an "executor" abstraction that runs blocks. Executors might be more general than needed but the default executors are useful to have:

  • dispatch_async to the main queue
  • dispatch_async to the default-pri background queue
  • run synchronously on the current queue where the previous task finished

`finally` to complement `then` and `catch`

For cleanup, it's often useful to register a callback that runs whenever the future resolves or rejects.

e.g.

[Promise until:^{ return tryAcquireRemoteLock(connectedSocket); }]
                catch:^{ return nil; }]
.finally(^{ [socket close]; });

Idea: throw/log unhandled errors on dealloc

This would be really useful to reduce silent failures. One of the roughest things about Promises (compared to C#/HHVM async functions) is that unhandled exceptions get swallowed. Many Promise implementations can't do much about this since someone could add a "catch" handler in the future. In Objective-C, though, we know that no one is going to add a handler after a Promise has been deallocated.

`+all:` and `+when:` should fulfill immediately if passed an empty array

I have an NSMutableArray that I add promises to and then call +when: to generate a new promise when all the promises have fulfilled. However, when the array contains no promises, +when: never fulfills.

Simple example:

Promise* p = [Promise when:@[]];
p.then(^{
    NSLog(@"here empty");
});

Promise* p2 = [Promise when:@[
                              [Promise promiseWithValue:@"hi"]
                              ]];
p2.then(^{
    NSLog(@"here with one");
});

Expected:
here empty
here with one

Actual:
here with one

This is easily fixed by adding the following to the top of +all:

if (count == 0)
        return [Promise promiseWithValue:@[]];

If you agree with the scenario, I'll fire off a PR.

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.