{"id":1107,"url":"https://github.com/malcommac/Hydra","last_synced_at":"2025-08-06T13:32:06.357Z","repository":{"id":47662313,"uuid":"78100813","full_name":"malcommac/Hydra","owner":"malcommac","description":"⚡️ Lightweight full-featured Promises, Async \u0026 Await Library in Swift","archived":false,"fork":false,"pushed_at":"2021-10-10T15:15:16.000Z","size":2226,"stargazers_count":1970,"open_issues_count":12,"forks_count":112,"subscribers_count":34,"default_branch":"master","last_synced_at":"2025-08-01T18:54:00.044Z","etag":null,"topics":["async","await","futures","gcd","promise-library","promises","swift"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/malcommac.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":"malcommac","custom":"https://www.paypal.com/paypalme2/danielemargutti"}},"created_at":"2017-01-05T09:50:26.000Z","updated_at":"2025-07-13T17:31:19.000Z","dependencies_parsed_at":"2022-09-12T15:32:19.521Z","dependency_job_id":null,"html_url":"https://github.com/malcommac/Hydra","commit_stats":null,"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"purl":"pkg:github/malcommac/Hydra","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/malcommac%2FHydra","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/malcommac%2FHydra/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/malcommac%2FHydra/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/malcommac%2FHydra/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/malcommac","download_url":"https://codeload.github.com/malcommac/Hydra/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/malcommac%2FHydra/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268699205,"owners_count":24292425,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-04T02:00:09.867Z","response_time":79,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","await","futures","gcd","promise-library","promises","swift"],"created_at":"2024-01-05T20:15:39.069Z","updated_at":"2025-08-06T13:32:06.036Z","avatar_url":"https://github.com/malcommac.png","language":"Swift","readme":"\u003cp align=\"center\" \u003e\n  \u003cimg src=\"banner.png\" width=300px alt=\"Hydra\" title=\"Hydra\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\u003cstrong\u003eLightweight full-featured Promises, Async \u0026 Await Library in Swift\u003c/strong\u003e\u003c/p\u003e\n\n## What's this?\n\nHydra is full-featured lightweight library which allows you to write better async code in Swift 3.x/4.x. It's partially based on [JavaScript A+](https://promisesaplus.com) specs and also implements modern construct like `await` (as seen in [Async/Await specification in ES8 (ECMAScript 2017)](https://github.com/tc39/ecmascript-asyncawait) or C#) which allows you to write async code in sync manner.\nHydra supports all sexiest operators like always, validate, timeout, retry, all, any, pass, recover, map, zip, defer and retry.  \nStarts writing better async code with Hydra!\n\nA more detailed look at how Hydra works can be found in [ARCHITECTURE](https://github.com/malcommac/Hydra/blob/master/ARCHITECTURE.md) file or on [Medium](https://medium.com/@danielemargutti/hydra-promises-swift-c6319f6a6209).\n\n## ❤️ Your Support\n\n*Hi fellow developer!*  \nYou know, maintaing and developing tools consumes resources and time. While I enjoy making them **your support is foundamental to allow me continue its development**.  \n\nIf you are using SwiftLocation or any other of my creations please consider the following options:\n\n- [**Make a donation with PayPal**](https://www.paypal.com/paypalme/danielemargutti/20)\n- [**Become a Sponsor**](https://github.com/sponsors/malcommac)\n\n- [Follow Me](https://github.com/malcommac)\n\n## Introduction\n\n* **[What's a Promise](#whatspromise)**\n* **[Updating to \u003e=0.9.7](#updating097)**\n* **[Create a Promise](#createpromise)**\n* **[How to use a Promise](#howtousepromise)**\n* **[Chaining Multiple Promises](#chaining)**\n* **[Cancellable Promises](#cancellablepromises)**\n* **[Await \u0026 Async: async code in sync manner](#awaitasync)**\n* **[Await an `zip` operator to resolve all promises](#allawait)**\n* **[All Features](#allfeatures)**\n\t* **[always](#always)**\n\t* **[validate](#validate)**\n\t* **[timeout](#timeout)**\n\t* **[all](#all)**\n\t* **[any](#any)**\n\t* **[pass](#pass)**\n\t* **[recover](#recover)**\n\t* **[map](#map)**\n\t* **[zip](#zip)**\n\t* **[defer](#defer)**\n\t* **[retry](#retry)**\n\t* **[cancel](#cancel)**\n* **[Chaining Promises with different `Value` types](#chainingdifferentpromises)**\n* **[Installation (CocoaPods, SwiftPM and Carthage)](#installation)**\n* **[Requirements](#requirements)**\n* **[Credits](#credits)**\n\n\u003ca name=\"whatspromise\" /\u003e\n\n## What's a Promise?\nA Promise is a way to represent a value that will exists, or will fail with an error, at some point in the future. You can think about it as a Swift's `Optional`: it may or may not be a value. A more detailed article which explain how Hydra was implemented can be [found here](https://github.com/malcommac/Hydra/blob/master/ARCHITECTURE.md).\n\nEach Promise is strong-typed: this mean you create it with the value's type you are expecting for and you will be sure to receive it when Promise will be resolved (the exact term is `fulfilled`).\n\nA Promise is, in fact, a proxy object; due to the fact the system knows what success value look like, composing asynchronous operation is a trivial task; with Hydra you can:\n\n- create a chain of dependent async operation with a single completion task and a single error handler.\n- resolve many independent async operations simultaneously and get all values at the end\n- retry or recover failed async operations\n- write async code as you may write standard sync code\n- resolve dependent async operations by passing the result of each value to the next operation, then get the final result\n- avoid callbacks, pyramid of dooms and make your code cleaner!\n\n\u003ca name=\"updating097\" /\u003e\n\n## Updating to \u003e=0.9.7\n\nSince 0.9.7 Hydra implements Cancellable Promises. In order to support this new feature we have slightly modified the `Body` signature of the `Promise`; in order to make your source code compatible you just need to add the third parameter along with `resolve`,`reject`: `operation`.\n`operation` encapsulate the logic to support `Invalidation Token`. It's just and object of type `PromiseStatus` you can query to see if a Promise is marked to be cancelled from the outside.\nIf you are not interested in using it in your Promise declaration just mark it as `_`.\n\nTo sum up your code:\n\n```swift\nreturn Promise\u003cInt\u003e(in: .main, token: token, { resolve, reject in ...\n```\n\nneeds to be:\n\n```swift\nreturn Promise\u003cInt\u003e(in: .main, token: token, { resolve, reject, operation in // or resolve, reject, _\n```\n\n\u003ca name=\"createpromise\" /\u003e\n\n## Create a Promise\nCreating a Promise is trivial; you need to specify the `context` (a GCD Queue) in which your async operations will be executed in and add your own async code as `body` of the Promise.\n\nThis is a simple async image downloader:\n\n```swift\nfunc getImage(url: String) -\u003e Promise\u003cUIImage\u003e {\n    return Promise\u003cUIImage\u003e(in: .background, { resolve, reject, _ in\n        self.dataTask(with: request, completionHandler: { data, response, error in\n            if let error = error {\n                reject(error)\n            } else if let data = data, let response = response as? HTTPURLResponse {\n                resolve((data, response))\n            } else {\n                reject(\"Image cannot be decoded\")\n            }\n        }).resume()\n    })\n}\n```\n\nYou need to remember only few things:\n\n- a Promise is created with a type: this is the object's type you are expecting from it once fulfilled. In our case we are expecting an `UIImage` so our Promise is `Promise\u003cUIImage\u003e` (if a promise fail returned error must be conform to Swift's `Error` protocol)\n- your async code (defined into the Promise's `body`) must alert the promise about its completion; if you have the fulfill value you will call `resolve(yourValue)`; if an error has occurred you can call `reject(occurredError)` or throw it using Swift's `throw occurredError`.\n- the `context` of a Promise define the Grand Central Dispatch's queue in which the async code will be executed in; you can use one of the defined queues (`.background`,`.userInitiated` etc. [Here you can found](http://www.appcoda.com/grand-central-dispatch/) a nice tutorial about this topic)\n\n\u003ca name=\"howtousepromise\" /\u003e\n\n## How to use a Promise\nUsing a Promise is even easier.  \nYou can get the result of a promise by using `then` function; it will be called automatically when your Promise fullfill with expected value.\nSo:\n\n```swift\ngetImage(url).then(.main, { image in\n\tmyImageView.image = image\n})\n```\n\nAs you can see even `then` may specify a context (by default - if not specified - is the main thread): this represent the GCD queue in which the code of the then's block will be executed (in our case we want to update an UI control so we will need to execute it in `.main` thread).\n\nBut what happened if your Promise fail due to a network error or if the image is not decodable? `catch` func allows you to handle Promise's errors (with multiple promises you may also have a single errors entry point and reduce the complexity).\n\n```swift\ngetImage(url).then(.main, { image in\n\tmyImageView.image = image\n}).catch(.main, { error in\n\tprint(\"Something bad occurred: \\(error)\")\n})\n```\n\u003ca name=\"chaining\" /\u003e\n\n## Chaining Multiple Promises\nChaining Promises is the next step thought mastering Hydra. Suppose you have defined some Promises:\n\n```swift\nfunc loginUser(_ name:String, _ pwd: String)-\u003ePromise\u003cUser\u003e\nfunc getFollowers(user: User)-\u003ePromise\u003c[Follower]\u003e\nfunc unfollow(followers: [Follower])-\u003ePromise\u003cInt\u003e\n```\n\nEach promise need to use the fulfilled value of the previous; plus an error in one of these should interrupt the entire chain.  \nDoing it with Hydra is pretty straightforward:\n\n```swift\nloginUser(username,pass).then(getFollowers).then(unfollow).then { count in\n\tprint(\"Unfollowed \\(count) users\")\n}.catch { err in\n\t// Something bad occurred during these calls\n}\n``` \n\nEasy uh? (Please note: in this example context is not specified so the default `.main` is used instead).\n\n\u003ca name=\"cancellablepromises\" /\u003e\n\n## Cancellable Promises\n\nCancellable Promises are a very sensitive task; by default Promises are not cancellable. Hydra allows you to cancel a promise from the outside by implementing the `InvalidationToken`. `InvalidationToken` is a concrete open class which is conform to the `InvalidatableProtocol` protocol.\nIt must implement at least one `Bool` property called `isCancelled`.\n\nWhen `isCancelled` is set to `true` it means someone outside the promise want to cancel the task.\n\nIt's your responsibility to check from inside the `Promise`'s body the status of this variable by asking to `operation.isCancelled`.\nIf `true` you can do all your best to cancel the operation; at the end of your operations just call `cancel()` and stop the workflow.\n\nYour promise must be also initialized using this token instance.\n\nThis is a concrete example with `UITableViewCell`: working with table cells, often the result of a promise needs to be ignored. To do this, each cell can hold on to an `InvalidationToken`. An `InvalidationToken` is an execution context that can be invalidated. If the context is invalidated, then the block that is passed to it will be discarded and not executed.\n\nTo use this with table cells, the queue should be invalidated and reset on `prepareForReuse()`.\n\n```swift\nclass SomeTableViewCell: UITableViewCell {\n    var token = InvalidationToken()\n\n\tfunc setImage(atURL url: URL) {\n\t\tdownloadImage(url).then(in: .main, { image in\n\t\t\tself.imageView.image = image\n\t\t})\n\t}\n\n\toverride func prepareForReuse() {\n\t\tsuper.prepareForReuse()\n\t\ttoken.invalidate() // stop current task and ignore result\n\t\ttoken = InvalidationToken() // new token\n\t}\n\n\tfunc downloadImage(url: URL) -\u003e Promise\u003cUIImage\u003e {\n\t\treturn Promise\u003cSomething\u003e(in: .background, token: token, { (resolve, reject, operation) in\n\t\t// ... your async operation\n\n\t\t// somewhere in your Promise's body, for example in download progression\n\t\t// you should check for the status of the operation.\n\t\tif operation.isCancelled {\n\t\t\t// operation should be cancelled\n\t\t\t// do your best to cancel the promise's task\n\t\t\toperation.cancel() // request to mark the Promise as cancelled\n\t\t\treturn // stop the workflow! it's important\n\t\t}\n\t\t// ... your async operation\n\t\t})\n\t}\n}\n```\n\n\u003ca name=\"awaitasync\" /\u003e\n\n## Await \u0026 Async: async code in sync manner\nHave you ever dream to write asynchronous code like its synchronous counterpart? Hydra was heavily inspired by [Async/Await specification in ES8 (ECMAScript 2017) ](https://github.com/tc39/ecmascript-asyncawait) which provides a powerful way to write async doe in a sequential manner.\n\nUsing `async` and `await` is pretty simple.\n\n\u003e NOTE: Since Hydra 2.0.6 the await function is available under Hydra.await() function in order to supress the Xcode 12.5+ warning (await will become a Swift standard function soon!)\n\nFor example the code above can be rewritten directly as:\n\n```swift\n// With `async` we have just defined a Promise which will be executed in a given\n// context (if omitted `background` thread is used) and return an Int value.\nlet asyncFunc = async({ _ -\u003e Int in // you must specify the return of the Promise, here an Int\n\t// With `await` the async code is resolved in a sync manner\n\tlet loggedUser = try Hydra.await(loginUser(username,pass))\n\t// one promise...\n\tlet followersList = try Hydra.await(getFollowers(loggedUser))\n\t// after another...\n\tlet countUnfollowed = try Hydra.await(unfollow(followersList))\n\t// ... linearly\n\t// Then our async promise will be resolved with the end value\n\treturn countUnfollowed\n}).then({ value in // ... and, like a promise, the value is returned\n\tprint(\"Unfollowed \\(value) users\")\n})\n```\n\nLike magic! Your code will run in `.background` thread and you will get the result of each call only when it will be fulfilled. Async code in sync sauce!\n\n**Important Note**: `await` is a blocking/synchronous function implemented using semaphore. Therefore, it should never be called in main thread; this is the reason we have used `async` to encapsulate it. Doing it in main thread will also block the UI.\n\n`async` func can be used in two different options:\n- it can create and return a promise (as you have seen above)\n- it can be used to simply execute a block of code (as you will see below)\n\nAs we said we can also use `async` with your own block (without using promises); `async` accepts the context (a GCD queue) and optionally a start delay interval.\nBelow an example of the async function which will be executed without delay in background:\n\n```swift\nasync({\n\tprint(\"And now some intensive task...\")\n\tlet result = try! Hydra.await(.background, { resolve,reject, _ in\n\t\tdelay(10, context: .background, closure: { // jut a trick for our example\n\t\t\tresolve(5)\n\t\t})\n\t})\n\tprint(\"The result is \\(result)\")\n})\n```\n\nThere is also an await operator:\n* **await with throw**: `..` followed by a Promise instance: this operator must be prefixed by `try` and should use `do/catch` statement in order to handle rejection of the Promise.\n* **await without throw**: `..!` followed by a Promise instance: this operator does not throw exceptions; in case of promise's rejection result is nil instead.\n\nExamples:\n```swift\nasync({\n\t// AWAIT OPERATOR WITH DO/CATCH: `..`\n\tdo {\n\t\tlet result_1 = try ..asyncOperation1()\n\t\tlet result_2 = try ..asyncOperation2(result_1) // result_1 is always valid\n\t} catch {\n\t\t// something goes bad with one of these async operations\n\t}\n})\n\n// AWAIT OPERATOR WITH NIL-RESULT: `..!`\nasync({\n\tlet result_1 = ..!asyncOperation1() // may return nil if promise fail. does not throw!\n\tlet result_2 = ..!asyncOperation2(result_1) // you must handle nil case manually\n})\n```\nWhen you use these methods and you are doing asynchronous, be careful to do nothing in the main thread, otherwise you risk to enter in a deadlock situation.\n\nThe last example show how to use cancellable `async`:\n\n```swift\nfunc test_invalidationTokenWithAsyncOperator() {\n\n// create an invalidation token\nlet invalidator: InvalidationToken = InvalidationToken()\n\nasync(token: invalidator, { status -\u003e String in\n\tThread.sleep(forTimeInterval: 2.0)\n\tif status.isCancelled {\n\t\tprint(\"Promise cancelled\")\n\t} else {\n\t\tprint(\"Promise resolved\")\n\t}\n\treturn \"\" // read result\n}).then { _ in\n\t// read result\n}\n\n// Anytime you can send a cancel message to invalidate the promise\ninvalidator.invalidate()\n}\n```\n\n\u003ca name=\"allawait\" /\u003e\n\n## Await an `zip` operator to resolve all promises\n\nAwait can be also used in conjuction with zip to resolve all promises from a list:\n\n```swift\nlet (resultA,resultB) = Hydra.await(zip(promiseA,promiseB))\nprint(resultA)\nprint(resultB)\n```\n\n\u003ca name=\"allfeature\" /\u003e\n\n## All Features\nBecause promises formalize how success and failure blocks look, it's possible to build behaviors on top of them.\nHydra supports:\n\n- `always`: allows you to specify a block which will be always executed both for `fulfill` and `reject` of the Promise\n- `validate`: allows you to specify a predica block; if predicate return `false` the Promise fails.\n- `timeout`: add a timeout timer to the Promise; if it does not fulfill or reject after given interval it will be marked as rejected.\n- `all`: create a Promise that resolved when the list of passed Promises resolves (promises are resolved in parallel). Promise also reject as soon as a promise reject for any reason.\n- `any`: create a Promise that resolves as soon as one passed from list resolves. It also reject as soon as a promise reject for any reason.\n- `pass`: Perform an operation in the middle of a chain that does not affect the resolved value but may reject the chain.\n- `recover`: Allows recovery of a Promise by returning another Promise if it fails.\n- `map`: Transform items to Promises and resolve them (in paralle or in series)\n- `zip`: Create a Promise tuple of a two promises\n- `defer`: defer the execution of a Promise by a given time interval.\n- `cancel`: cancel is called when a promise is marked as `cancelled` using `operation.cancel()`\n\n\u003ca name=\"always\" /\u003e\n\n### always\n`always` func is very useful if you want to execute code when the promise fulfills — regardless of whether it succeeds or fails.\n\n```swift\nshowLoadingHUD(\"Logging in...\")\nloginUser(username,pass).then { user in\n\tprint(\"Welcome \\(user.username)\")\n}.catch { err in\n \tprint(\"Cannot login \\(err)\")\n}.always {\n \thideLoadingHUD()\n}\n```\n\u003ca name=\"validate\" /\u003e\n\n### validate\n`validate` is a func that takes a predicate, and rejects the promise chain if that predicate fails.\n\n```swift\ngetAllUsersResponse().validate { httpResponse in\n\tguard let httpResponse.statusCode == 200 else {\n\t\treturn false\n\t}\n\treturn true\n}.then { usersList in\n\t// do something\n}.catch { error in\n\t// request failed, or the status code was != 200\n}\n\n```\n\n\u003ca name=\"timeout\" /\u003e\n\n### timeout\n`timeout` allows you to attach a timeout timer to a Promise; if it does not resolve before elapsed interval it will be rejected with `.timeoutError`.\n\n```swift\nloginUser(username,pass).timeout(.main, 10, .MyCustomTimeoutError).then { user in\n\t// logged in\n}.catch { err in\n\t// an error has occurred, may be `MyCustomTimeoutError\n}\n```\n\n\u003ca name=\"all\" /\u003e\n\n### all\n`all` is a static method that waits for all the promises you give it to fulfill, and once they have, it fulfills itself with the array of all fulfilled values (in order).\n\nIf one Promise fail the chain fail with the same error.\n\nExecution of all promises is done in parallel.\n\n```swift\nlet promises = usernameList.map { return getAvatar(username: $0) }\nall(promises).then { usersAvatars in\n\t// you will get an array of UIImage with the avatars of input\n\t// usernames, all in the same order of the input.\n\t// Download of the avatar is done in parallel in background!\n}.catch { err in\n\t// something bad has occurred\n}\n```\n\nIf you add promise execution concurrency restriction to `all` operator  to avoid many usage of resource, `concurrency` option is it.\n\n```swift\nlet promises = usernameList.map { return getAvatar(username: $0) }\nall(promises, concurrency: 4).then { usersAvatars in\n\t// results of usersAvatars is same as `all` without concurrency.\n}.catch { err in\n\t// something bad has occurred\n}\n```\n\n\u003ca name=\"any\" /\u003e\n\n### any\n`any` easily handle race conditions: as soon as one Promise of the input list resolves the handler is called and will never be called again.\n\n```swift\nlet mirror_1 = \"https://mirror1.mycompany.com/file\"\nlet mirror_2 = \"https://mirror2.mycompany.com/file\"\n\nany(getFile(mirror_1), getFile(mirror_2)).then { data in\n\t// the first fulfilled promise also resolve the any Promise\n\t// handler is called exactly one time!\n}\n```\n\n\u003ca name=\"pass\" /\u003e\n\n### pass\n`pass` is useful for performing an operation in the middle of a promise chain without changing the type of the Promise.\nYou may also reject the entire chain.\nYou can also return a Promise from the tap handler and the chain will wait for that promise to resolve (see the second `then` in the example below).\n\n```swift\nloginUser(user,pass).pass { userObj in \n\tprint(\"Fullname is \\(user.fullname)\")\n}.then { userObj in\n\tupdateLastActivity(userObj)\n}.then { userObj in\n\tprint(\"Login succeded!\")\n}\n```\n\u003ca name=\"recover\" /\u003e\n\n### recover\n`recover` allows you to recover a failed Promise by returning another.\n\n```swift\nlet promise = Promise\u003cInt\u003e(in: .background, { fulfill, reject in\n\treject(AnError)\n}).recover({ error in\n    return Promise(in: .background, { (fulfill, reject) in\n\t\tfulfill(value)\n    })\n})\n```\n\u003ca name=\"map\" /\u003e\n\n### map\nMap is used to transform a list of items into promises and resolve them in parallel or serially.\n\n```swift\n[urlString1,urlString2,urlString3].map {\n\treturn self.asyncFunc2(value: $0)\n}.then(.main, { dataArray in\n\t// get the list of all downloaded data from urls\n}).catch({\n\t// something bad has occurred\n})\n```\n\n\u003ca name=\"zip\" /\u003e\n\n### zip\n`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.\n\n```swift\nzip(a: getUserProfile(user), b: getUserAvatar(user), c: getUserFriends(user))\n  .then { profile, avatar, friends in\n\t// ... let's do something\n}.catch {\n\t// something bad as occurred. at least one of given promises failed\n}\n```\n\n\u003ca name=\"defer\" /\u003e\n\n### defer\nAs name said, `defer` delays the execution of a Promise chain by some number of seconds from current time.\n\n```swift\nasyncFunc1().defer(.main, 5).then...\n```\n\n### retry\n`retry` operator allows you to execute source chained promise if it ends with a rejection.\nIf reached the attempts the promise still rejected chained promise is also rejected along with the same source error.  \nRetry also support `delay` parameter which specify the number of seconds to wait before a new attempt (2.0.4+).\n\n```swift\n// try to execute myAsyncFunc(); if it fails the operator try two other times\n// If there is not luck for you the promise itself fails with the last catched error.\nmyAsyncFunc(param).retry(3).then { value in\n\tprint(\"Value \\(value) got at attempt #\\(currentAttempt)\")\n}.catch { err in\n\tprint(\"Failed to get a value after \\(currentAttempt) attempts with error: \\(err)\")\n}\n```\n\nConditional retry allows you to control retryable if it ends with a rejection.\n\n```swift\n// If myAsyncFunc() fails the operator execute the condition block to check retryable.\n// If return false in condition block, promise state rejected with last catched error.\nmyAsyncFunc(param).retry(3) { (remainAttempts, error) -\u003e Bool in\n  return error.isRetryable\n}.then { value in\n\tprint(\"Value \\(value) got at attempt #\\(currentAttempt)\")\n}.catch { err in\n\tprint(\"Failed to get a value after \\(currentAttempt) attempts with error: \\(err)\")\n}\n```\n\n\u003ca name=\"cancel\" /\u003e\n\n### cancel\n`cancel` is called when a promise is marked as `cancelled` from the Promise's body by calling the `operation.cancel()` function. See the **[Cancellable Promises](#cancellablepromises)** for more info.\n\n```swift\nasyncFunc1().cancel(.main, {\n\t// promise is cancelled, do something\n}).then...\n```\n\n\u003ca name=\"chainingdifferentpromises\" /\u003e\n\n## Chaining Promises with different `Value` types\n\nSometimes you may need to chain (using one of the available operators, like `all` or `any`) promises which returns different kind of values. Due to the nature of Promise you are not able to create an array of promises with different result types.\nHowever thanks to `void` property you are able to transform promise instances to generic `void` result type.\nSo, for example, you can execute the following `Promises` and return final values directly from the Promise's `result` property.\n\n```swift\nlet op_1: Promise\u003cUser\u003e = asyncGetCurrentUserProfile()\nlet op_2: Promise\u003cUIImage\u003e = asyncGetCurrentUserAvatar()\nlet op_3: Promise\u003c[User]\u003e = asyncGetCUrrentUserFriends()\n\nall(op_1.void,op_2.void,op_3.void).then { _ in\n\tlet userProfile = op_1.result\n\tlet avatar = op_2.result\n\tlet friends = op_3.result\n}.catch { err in\n\t// do something\n}\n```\n\n\u003ca name=\"installation\" /\u003e\n\n## Installation\nYou can install Hydra using CocoaPods, Carthage and Swift package manager\n\n- **Swift 3.x**: Latest compatible is 1.0.2 `pod 'HydraAsync', ~\u003e '1.0.2'`\n- **Swift 4.x**: 1.2.1 or later `pod 'HydraAsync'`\n\n### CocoaPods\n    use_frameworks!\n    pod 'HydraAsync'\n    \n### Carthage\n    github 'malcommac/Hydra'\n\n### Swift Package Manager\nAdd Hydra as dependency in your `Package.swift`\n\n```\n  import PackageDescription\n\n  let package = Package(name: \"YourPackage\",\n    dependencies: [\n      .Package(url: \"https://github.com/malcommac/Hydra.git\", majorVersion: 0),\n    ]\n  )\n```\n\n**Consider ❤️ [support the development](#support) of this library!**\n\n\u003ca name=\"requirements\" /\u003e\n\n## Requirements\n\nCurrent version is compatible with:\n\n* Swift 5.x\n* iOS 9.0 or later\n* tvOS 9.0 or later\n* macOS 10.10 or later\n* watchOS 2.0 or later\n* Linux compatible environments\n\n\u003ca name=\"credits\" /\u003e\n\n## Contributing\n\n- If you **need help** or you'd like to **ask a general question**, open an issue.\n- If you **found a bug**, open an issue.\n- If you **have a feature request**, open an issue.\n- If you **want to contribute**, submit a pull request.\n\n## Copyright \u0026 Acknowledgements\n\nSwiftLocation is currently owned and maintained by Daniele Margutti.  \nYou can follow me on Twitter [@danielemargutti](http://twitter.com/danielemargutti).  \nMy web site is [https://www.danielemargutti.com](https://www.danielemargutti.com) \n\nThis software is licensed under [MIT License](LICENSE.md).\n\n***Follow me on:***  \n- 💼 [Linkedin](https://www.linkedin.com/in/danielemargutti/)  \n- 🐦 [Twitter](https://twitter.com/danielemargutti)\n\n```\n","funding_links":["https://github.com/sponsors/malcommac","https://www.paypal.com/paypalme2/danielemargutti","https://www.paypal.com/paypalme/danielemargutti/20"],"categories":["EventBus","Libs","HarmonyOS","Concurrency [🔝](#readme)","swift"],"sub_categories":["Getting Started","Concurrency","Windows Manager","Other free courses","Linter"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmalcommac%2FHydra","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmalcommac%2FHydra","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmalcommac%2FHydra/lists"}