Add PromiseKit dependency
- Added PromiseKit dependency
This commit is contained in:
201
Carthage/Checkouts/PromiseKit/Documentation/ObjectiveC.md
vendored
Normal file
201
Carthage/Checkouts/PromiseKit/Documentation/ObjectiveC.md
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
# Objective-C
|
||||
|
||||
PromiseKit has two promise classes:
|
||||
|
||||
* `Promise<T>` (Swift)
|
||||
* `AnyPromise` (Objective-C)
|
||||
|
||||
Each is designed to be an appropriate promise implementation for the strong points of its language:
|
||||
|
||||
* `Promise<T>` is strict, defined and precise.
|
||||
* `AnyPromise` is loose and dynamic.
|
||||
|
||||
Unlike most libraries, we have extensive bridging support, you can use PromiseKit
|
||||
in mixed projects with mixed language targets and mixed language libraries.
|
||||
|
||||
|
||||
# Using PromiseKit with Objective-C
|
||||
|
||||
`AnyPromise` is our promise class for Objective-C. It behaves almost identically to `Promise<T>`, our Swift promise class.
|
||||
|
||||
```objc
|
||||
myPromise.then(^(NSString *bar){
|
||||
return anotherPromise;
|
||||
}).then(^{
|
||||
//…
|
||||
}).catch(^(NSError *error){
|
||||
//…
|
||||
});
|
||||
```
|
||||
|
||||
You make new promises using `promiseWithResolverBlock`:
|
||||
|
||||
```objc
|
||||
- (AnyPromise *)myPromise {
|
||||
return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve){
|
||||
resolve(foo); // if foo is an NSError, rejects, else, resolves
|
||||
}];
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
You reject promises by throwing errors:
|
||||
|
||||
```objc
|
||||
myPromise.then(^{
|
||||
@throw [NSError errorWithDomain:domain code:code userInfo:nil];
|
||||
}).catch(^(NSError *error){
|
||||
//…
|
||||
});
|
||||
```
|
||||
#### :warning: Caution:
|
||||
ARC in Objective-C, unlike in Objective-C++, is not exception-safe by default.
|
||||
So, throwing an error will result in keeping a strong reference to the closure
|
||||
that contains the throw statement.
|
||||
This pattern will consequently result in memory leaks if you're not careful.
|
||||
|
||||
> *Note:* Only having a strong reference to the closure would result in memory leaks.
|
||||
> In our case, PromisKit automatically keeps a strong reference to the closure until it's released.
|
||||
|
||||
__Workarounds:__
|
||||
1. Return a Promise with value NSError\
|
||||
Instead of throwing a normal error, you can return a Promise with value NSError instead.
|
||||
|
||||
```objc
|
||||
myPromise.then(^{
|
||||
return [AnyPromise promiseWithValue:[NSError myCustomError]];
|
||||
}).catch(^(NSError *error){
|
||||
if ([error isEqual:[NSError myCustomError]]) {
|
||||
// In case, same error as the one we thrown
|
||||
return;
|
||||
}
|
||||
//…
|
||||
});
|
||||
```
|
||||
2. Enable ARC for exceptions in Objective-C (not recomended)\
|
||||
You can add this ```-fobjc-arc-exceptions to your``` to your compiler flags to enable ARC for exceptions.
|
||||
This is not recommended unless you've read the Apple documentation and are comfortable with the caveats.
|
||||
|
||||
For more details on ARC and exceptions:
|
||||
https://clang.llvm.org/docs/AutomaticReferenceCounting.html#exceptions
|
||||
|
||||
|
||||
---
|
||||
|
||||
One important feature is the syntactic flexability of your handlers:
|
||||
|
||||
```objc
|
||||
myPromise.then(^{
|
||||
// no parameters is fine
|
||||
});
|
||||
|
||||
myPromise.then(^(id foo){
|
||||
// one parameter is fine
|
||||
});
|
||||
|
||||
myPromise.then(^(id a, id b, id c){
|
||||
// up to three parameter is fine, no crash!
|
||||
});
|
||||
|
||||
myPromise.then(^{
|
||||
return @1; // return anything or nothing, it's fine, no crash
|
||||
});
|
||||
```
|
||||
|
||||
We do runtime inspection of the block you pass to achieve this magic.
|
||||
|
||||
---
|
||||
|
||||
Another important distinction is that the equivalent function to Swift’s `recover` is combined with `AnyPromise`’s `catch`. This is typical to other “dynamic” promise implementations and thus achieves our goal that `AnyPromise` is loose and dynamic while `Promise<T>` is strict and specific.
|
||||
|
||||
A sometimes unexpected consequence of this fact is that returning nothing from a `catch` *resolves* the returned promise:
|
||||
|
||||
```objc
|
||||
myPromise.catch(^{
|
||||
[UIAlertView …];
|
||||
}).then(^{
|
||||
// always executes!
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Another important distinction is that the `value` property returns even if the promise is rejected; in that case, it returns the `NSError` object with which the promise was rejected.
|
||||
|
||||
|
||||
# Bridging Between Objective-C & Swift
|
||||
|
||||
Let’s say you have:
|
||||
|
||||
```objc
|
||||
@interface Foo
|
||||
- (AnyPromise *)myPromise;
|
||||
@end
|
||||
```
|
||||
|
||||
Ensure that this interface is included in your bridging header. You can now use the
|
||||
following pattern in your Swift code:
|
||||
|
||||
```swift
|
||||
let foo = Foo()
|
||||
foo.myPromise.then { (obj: AnyObject?) -> Int in
|
||||
// it is not necessary to specify the type of `obj`
|
||||
// we just do that for demonstrative purposes
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Let’s say you have:
|
||||
|
||||
```swift
|
||||
@objc class Foo: NSObject {
|
||||
func stringPromise() -> Promise<String>
|
||||
func barPromise() -> Promise<Bar>
|
||||
}
|
||||
|
||||
@objc class Bar: NSObject { /*…*/ }
|
||||
```
|
||||
|
||||
Ensure that your project is generating a `…-Swift.h` header so that Objective-C can see your Swift code.
|
||||
|
||||
If you built this project and opened the `…-Swift.h` header, you would only see this:
|
||||
|
||||
```objc
|
||||
@interface Foo
|
||||
@end
|
||||
|
||||
@interface Bar
|
||||
@end
|
||||
```
|
||||
|
||||
That's because Objective-C cannot import Swift objects that are generic. So we need to write some stubs:
|
||||
|
||||
```swift
|
||||
@objc class Foo: NSObject {
|
||||
@objc func stringPromise() -> AnyPromise {
|
||||
return AnyPromise(stringPromise())
|
||||
}
|
||||
@objc func barPromise() -> AnyPromise {
|
||||
return AnyPromise(barPromise())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If we built this and opened our generated header, we would now see:
|
||||
|
||||
```objc
|
||||
@interface Foo
|
||||
- (AnyPromise *)stringPromise;
|
||||
- (AnyPromise *)barPromise;
|
||||
@end
|
||||
|
||||
@interface Bar
|
||||
@end
|
||||
```
|
||||
|
||||
Perfect.
|
||||
|
||||
Note that AnyPromise can only bridge objects that conform to `AnyObject` or derive from `NSObject`. This is a limitation of Objective-C.
|
||||
|
Reference in New Issue
Block a user