Skip to content

Commit

Permalink
Merge branch 'release/0.9.3'
Browse files Browse the repository at this point in the history
  • Loading branch information
malcommac committed Mar 6, 2017
2 parents e81d32a + b5afd65 commit fb4dbf3
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 12 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,20 @@

## CHANGELOG

* Version **[0.9.3](#093)**
* Version **[0.9.2](#092)**
* Version **[0.9.1](#091)**

<a name="093" />

## Hydra 0.9.3
---
- **Release Date**: 2017-03-06
- **Zipped Version**: [Download 0.9.3](https://github.com/malcommac/Hydra/releases/tag/0.9.3)

- [#15](https://github.com/malcommac/Hydra/pull/15) Added conditional block to retry operator to determine whether retry is possible
- [#14](https://github.com/malcommac/Hydra/pull/14) Minor fixes for documentation (`zip` and `all` funcs)

<a name="092" />

## Hydra 0.9.2
Expand Down
4 changes: 2 additions & 2 deletions Hydra/Hydra.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 3;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
Expand Down Expand Up @@ -503,7 +503,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 3;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
Expand Down
Binary file not shown.
2 changes: 1 addition & 1 deletion HydraAsync.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |spec|
spec.name = 'HydraAsync'
spec.version = '0.9.2'
spec.version = '0.9.3'
spec.summary = 'Promises & Await: Write better async in Swift'
spec.homepage = 'https://github.com/malcommac/Hydra'
spec.license = { :type => 'MIT', :file => 'LICENSE' }
Expand Down
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ Execution of all promises is done in parallel.

```swift
let promises = usernameList.map { return getAvatar(username: $0) }
Promise.all(promises).then { usersAvatars in
all(promises).then { usersAvatars in
// you will get an array of UIImage with the avatars of input
// usernames, all in the same order of the input.
// Download of the avatar is done in parallel in background!
Expand Down Expand Up @@ -375,7 +375,7 @@ Map is used to transform a list of items into promises and resolve them in paral
`zip` allows you to join different promises (2,3 or 4) and return a tuple with the result of them. Promises are resolved in parallel.

```swift
join(getUserProfile(user), getUserAvatar(user), getUserFriends(user))
Promise<Void>.zip(a: getUserProfile(user), b: getUserAvatar(user), c: getUserFriends(user))
.then { profile, avatar, friends in
// ... let's do something
}.catch {
Expand Down Expand Up @@ -406,6 +406,20 @@ myAsyncFunc(param).retry(3).then { value in
}
```

Conditional retry allows you to control retryable if it ends with a rejection.

```swift
// If myAsyncFunc() fails the operator execute the condition block to check retryable.
// If return false in condition block, promise state rejected with last catched error.
myAsyncFunc(param).retry(3) { (remainAttempts, error) -> Bool in
return error.isRetryable
}.then { value in
print("Value \(value) got at attempt #\(currentAttempt)")
}.catch { err in
print("Failed to get a value after \(currentAttempt) attempts with error: \(err)")
}
```

<a name="installation" />

## Installation
Expand Down
2 changes: 1 addition & 1 deletion Sources/Hydra/Promise+Always.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public extension Promise {
}
})

self.add(observers: onResolve,onReject)
self.add(observers: onResolve, onReject)
}
nextPromise.runBody()
self.runBody()
Expand Down
2 changes: 1 addition & 1 deletion Sources/Hydra/Promise+Catch.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public extension Promise {
}
resolve(())
})
self.add(observers: onResolve,onReject)
self.add(observers: onResolve, onReject)
}
nextPromise.runBody()
self.runBody()
Expand Down
19 changes: 16 additions & 3 deletions Sources/Hydra/Promise+Retry.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ public extension Promise {
/// If reached the attempts the promise still rejected chained promise is also rejected along with
/// the same source error.
///
/// - Parameter attempts: number of retry attempts for source promise (must be a number > 1, otherwise promise is rejected with `PromiseError.invalidInput` error.
/// - Parameters:
/// - attempts: number of retry attempts for source promise (must be a number > 1, otherwise promise is rejected with `PromiseError.invalidInput` error.
/// - condition: code block to check retryable source promise
/// - Returns: a promise which resolves when the first attempt to resolve source promise is succeded, rejects if none of the attempts ends with a success.
public func retry(_ attempts: Int = 3) -> Promise<Value> {
public func retry(_ attempts: Int = 3, _ condition: @escaping ((Int, Error) throws -> Bool) = { _ in true }) -> Promise<Value> {
guard attempts >= 1 else {
// Must be a valid attempts number
return Promise<Value>(rejected: PromiseError.invalidInput)
Expand All @@ -61,6 +63,17 @@ public extension Promise {
reject(error)
return
}
// If promise is rejected we will check condition that is retryable
do {
guard try condition(remainingAttempts, error) else {
reject(error)
return
}
} catch(_) {
// reject soruce promise error
reject(error)
return
}
// Reset the state of the promise
// (okay it's true, a Promise cannot change state as you know...this
// is a bit trick which will remain absolutely internal to the library itself)
Expand All @@ -69,7 +82,7 @@ public extension Promise {
self.runBody()
})
// Observe changes from source promise
self.add(observers: onResolve,onReject)
self.add(observers: onResolve, onReject)
self.runBody()
}
nextPromise.runBody()
Expand Down
4 changes: 2 additions & 2 deletions Sources/Hydra/Promise+Then.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public extension Promise {
// execute the promise's body and get the result of it
let pResolve = Observer<N>.onResolve(ctx, resolve)
let pReject = Observer<N>.onReject(ctx, reject)
chainedPromise.add(observers: pResolve,pReject)
chainedPromise.add(observers: pResolve, pReject)
chainedPromise.runBody()
} catch let error {
reject(error)
Expand All @@ -96,7 +96,7 @@ public extension Promise {
// Observe the reject of the self promise
let onReject = Observer<Value>.onReject(ctx, reject)

self.add(observers: onResolve,onReject)
self.add(observers: onResolve, onReject)
})
nextPromise.runBody()
self.runBody()
Expand Down
36 changes: 36 additions & 0 deletions Tests/HydraTests/HydraTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,42 @@ class HydraTestThen: XCTestCase {
waitForExpectations(timeout: expTimeout, handler: nil)
}

func test_retry_condition() {
let exp = expectation(description: "test_retry_condition")

let retryAttempts = 5
let successOnAttempt = 5
let retryableRemainAttempt = 2
var currentAttempt = 0
Promise<Int> { (resolve, reject) in
currentAttempt += 1
if currentAttempt < successOnAttempt {
print("attempt is \(currentAttempt)... reject")
reject(TestErrors.anotherError)
} else {
print("attempt is \(currentAttempt)... resolve")
resolve(5)
}
}.retry(retryAttempts) { (remainAttempts, error) -> Bool in
if remainAttempts > retryableRemainAttempt {
print("retry remainAttempts is \(remainAttempts)... true")
return true
} else {
print("retry remainAttempts is \(remainAttempts)... false")
return false
}
}.then { value in
print("value \(value) at attempt \(currentAttempt)")
XCTFail()
}.catch { err in
print("failed \(err) at attempt \(currentAttempt)")
XCTAssertEqual(currentAttempt, 3)
exp.fulfill()
}

waitForExpectations(timeout: expTimeout, handler: nil)
}

//MARK: Helper

func intFailedPromise(_ error: Error) -> Promise<Int> {
Expand Down

0 comments on commit fb4dbf3

Please sign in to comment.