diff --git a/CHANGELOG.md b/CHANGELOG.md
index 30d5b3d..3487e7d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,9 +4,20 @@
## CHANGELOG
+* Version **[0.9.3](#093)**
* Version **[0.9.2](#092)**
* Version **[0.9.1](#091)**
+
+
+## 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)
+
## Hydra 0.9.2
diff --git a/Hydra/Hydra.xcodeproj/project.pbxproj b/Hydra/Hydra.xcodeproj/project.pbxproj
index e71291f..fa03812 100644
--- a/Hydra/Hydra.xcodeproj/project.pbxproj
+++ b/Hydra/Hydra.xcodeproj/project.pbxproj
@@ -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;
@@ -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;
diff --git a/Hydra/Hydra.xcodeproj/project.xcworkspace/xcuserdata/danielemm.xcuserdatad/UserInterfaceState.xcuserstate b/Hydra/Hydra.xcodeproj/project.xcworkspace/xcuserdata/danielemm.xcuserdatad/UserInterfaceState.xcuserstate
index afdc086..cf3e555 100644
Binary files a/Hydra/Hydra.xcodeproj/project.xcworkspace/xcuserdata/danielemm.xcuserdatad/UserInterfaceState.xcuserstate and b/Hydra/Hydra.xcodeproj/project.xcworkspace/xcuserdata/danielemm.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/HydraAsync.podspec b/HydraAsync.podspec
index 2d50f0b..9833626 100644
--- a/HydraAsync.podspec
+++ b/HydraAsync.podspec
@@ -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' }
diff --git a/README.md b/README.md
index 270eb2c..dedc93e 100644
--- a/README.md
+++ b/README.md
@@ -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!
@@ -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.zip(a: getUserProfile(user), b: getUserAvatar(user), c: getUserFriends(user))
.then { profile, avatar, friends in
// ... let's do something
}.catch {
@@ -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)")
+}
+```
+
## Installation
diff --git a/Sources/Hydra/Promise+Always.swift b/Sources/Hydra/Promise+Always.swift
index f9b7d00..591746a 100644
--- a/Sources/Hydra/Promise+Always.swift
+++ b/Sources/Hydra/Promise+Always.swift
@@ -64,7 +64,7 @@ public extension Promise {
}
})
- self.add(observers: onResolve,onReject)
+ self.add(observers: onResolve, onReject)
}
nextPromise.runBody()
self.runBody()
diff --git a/Sources/Hydra/Promise+Catch.swift b/Sources/Hydra/Promise+Catch.swift
index cefefe0..5d4a492 100644
--- a/Sources/Hydra/Promise+Catch.swift
+++ b/Sources/Hydra/Promise+Catch.swift
@@ -56,7 +56,7 @@ public extension Promise {
}
resolve(())
})
- self.add(observers: onResolve,onReject)
+ self.add(observers: onResolve, onReject)
}
nextPromise.runBody()
self.runBody()
diff --git a/Sources/Hydra/Promise+Retry.swift b/Sources/Hydra/Promise+Retry.swift
index 8bfc974..3a859cc 100644
--- a/Sources/Hydra/Promise+Retry.swift
+++ b/Sources/Hydra/Promise+Retry.swift
@@ -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 {
+ public func retry(_ attempts: Int = 3, _ condition: @escaping ((Int, Error) throws -> Bool) = { _ in true }) -> Promise {
guard attempts >= 1 else {
// Must be a valid attempts number
return Promise(rejected: PromiseError.invalidInput)
@@ -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)
@@ -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()
diff --git a/Sources/Hydra/Promise+Then.swift b/Sources/Hydra/Promise+Then.swift
index 0a96e79..411c6de 100644
--- a/Sources/Hydra/Promise+Then.swift
+++ b/Sources/Hydra/Promise+Then.swift
@@ -86,7 +86,7 @@ public extension Promise {
// execute the promise's body and get the result of it
let pResolve = Observer.onResolve(ctx, resolve)
let pReject = Observer.onReject(ctx, reject)
- chainedPromise.add(observers: pResolve,pReject)
+ chainedPromise.add(observers: pResolve, pReject)
chainedPromise.runBody()
} catch let error {
reject(error)
@@ -96,7 +96,7 @@ public extension Promise {
// Observe the reject of the self promise
let onReject = Observer.onReject(ctx, reject)
- self.add(observers: onResolve,onReject)
+ self.add(observers: onResolve, onReject)
})
nextPromise.runBody()
self.runBody()
diff --git a/Tests/HydraTests/HydraTests.swift b/Tests/HydraTests/HydraTests.swift
index 4347600..4325311 100644
--- a/Tests/HydraTests/HydraTests.swift
+++ b/Tests/HydraTests/HydraTests.swift
@@ -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 { (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 {