{"id":933,"url":"https://github.com/AsyncNinja/AsyncNinja","last_synced_at":"2025-08-06T13:32:04.788Z","repository":{"id":55259388,"uuid":"65158825","full_name":"AsyncNinja/AsyncNinja","owner":"AsyncNinja","description":"A complete set of primitives for concurrency and reactive programming on Swift","archived":false,"fork":false,"pushed_at":"2021-10-03T20:19:59.000Z","size":2145,"stargazers_count":156,"open_issues_count":1,"forks_count":16,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-11-07T19:20:31.487Z","etag":null,"topics":["async","channel","concurrency","concurrency-library","functional","future","reactive","swift"],"latest_commit_sha":null,"homepage":"https://async.ninja/","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/AsyncNinja.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.MD","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-08-07T23:29:44.000Z","updated_at":"2024-11-06T16:08:48.000Z","dependencies_parsed_at":"2022-08-14T18:12:14.327Z","dependency_job_id":null,"html_url":"https://github.com/AsyncNinja/AsyncNinja","commit_stats":null,"previous_names":[],"tags_count":41,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AsyncNinja%2FAsyncNinja","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AsyncNinja%2FAsyncNinja/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AsyncNinja%2FAsyncNinja/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AsyncNinja%2FAsyncNinja/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AsyncNinja","download_url":"https://codeload.github.com/AsyncNinja/AsyncNinja/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228689662,"owners_count":17957605,"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","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","channel","concurrency","concurrency-library","functional","future","reactive","swift"],"created_at":"2024-01-05T20:15:34.946Z","updated_at":"2024-12-09T14:30:43.436Z","avatar_url":"https://github.com/AsyncNinja.png","language":"Swift","funding_links":[],"categories":["Concurrency","Libs","Concurrency [🔝](#readme)","The Index"],"sub_categories":["Linter","Concurrency","Other free courses","Workflow-enabler Frameworks"],"readme":"\n# ![AsyncNinja Title](title_image.png)\n## A complete set of primitives for concurrency and reactive programming on Swift\n\n[![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/AsyncNinja/Lobby)\n[![CocoaPods](https://img.shields.io/cocoapods/v/AsyncNinja.svg)](https://cocoapods.org/pods/AsyncNinja)\n[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)\n[![Build Status](https://travis-ci.org/AsyncNinja/AsyncNinja.svg?branch=master)](https://travis-ci.org/AsyncNinja)\n\n* **1.4.0 is the latest and greatest, but only for Swift 4.2 and 5.0**\n* **use 1.3.0 is for Swift 4.0+**\n* **use 1.2.4 for latest release for Swift 3**\n\n|   | Features   |\n|---|---|\n| 🦄 \u003cbr/\u003e powerful primitives       | `Future`, `Promise`, `Channel`, `Producer`, `Sink`, `Cache`, ...           |\n| 🤘 \u003cbr/\u003e versatile transformations | `map`, `filter`, `recover`, `debounce`, `distinct`, ...   |\n| ✌️ \u003cbr/\u003e convenient combination    | `flatMap`, `merge`, `zip`, `sample`, `scan`, `reduce`, ...   |\n| 🙌 \u003cbr/\u003e improves existing things  | Key-Value Observing,  target-action, notifications, bindings            |\n| 🍳 \u003cbr/\u003e less boilerplate code     | neat cancellation, threading, memory manament                  |\n| 🕶 \u003cbr/\u003e extendable                | powerful extensions for `URLSession`, UI controls, `CoreData`, ... |\n| 🍱 \u003cbr/\u003e all platforms \u003cbr/\u003e       |  🖥 macOS 10.10+  📱 iOS 8.0+ 📺 tvOS 9.0+ ⌚️ watchOS 2.0+ 🐧 Linux       |\n| 🤓 \u003cbr/\u003e documentation             | 100% + sample code, **[see full documentation](http://docs.async.ninja/)** |\n| 🔩 \u003cbr/\u003e simple integration        | [SPM](Documentation/Integration.md#using-swift-package-manager), [CocoaPods](Documentation/Integration.md#cocoapods), [Carthage](Documentation/Integration.md#сarthage) |\n\n* Related articles\n\t* Moving to nice asynchronous Swift code: [GitHub](https://github.com/AsyncNinja/article-moving-to-nice-asynchronous-swift-code/blob/master/ARTICLE.md), [Medium](https://medium.com/@AntonMironov/moving-to-nice-asynchronous-swift-code-7b0cb2eadde1)\n* [Known users](known_users.md)\n\n## Communication\n\n* [GitHub issues](https://github.com/AsyncNinja/AsyncNinja/issues/new)\n* [Gitter.im](https://gitter.im/AsyncNinja/Lobby)\n* [Considering contribution?](CONTRIBUTING.md)\n\n## Reactive Programming\n\n#### reactive properties\n\n```swift\nlet searchResults = searchBar.rp.text\n  .debounce(interval: 0.3)\n  .distinct()\n  .flatMap(behavior: .keepLatestTransform) { (query) -\u003e Future\u003c[SearchResult]\u003e in\n    return query.isEmpty\n      ? .just([])\n      : searchGitHub(query: query).recover([])\n  }\n```\n\n#### bindings\n\n- unbinds automatically\n- dispatches to a correct queue automatically\n- no  `.observeOn(MainScheduler.instance)` and `.disposed(by: disposeBag)`\n\n```swift\nclass MyViewController: UIViewController {\n  /* ... */\n  @IBOutlet weak var myLabel: UILabel!\n\n  override func viewDidLoad() {\n    super.viewDidLoad()\n    UIDevice.current.rp.orientation\n      .map { $0.description }\n      .bind(myLabel.rp.text)\n  }\n  \n  /* ... */\n}\n```\n\n#### contexts usage\n\n- no `[weak self]`\n- no `DispatchQueue.main.async { ... }`\n- no  `.observeOn(MainScheduler.instance)`\n\n```swift\nclass MyViewController: NSViewController {\n  let service: MyService\n\n  /* ... */\n  \n  func fetchAndPresentItems(for request: Request) {\n    service.perform(request: request)\n      .map(context: self, executor: .primary) { (self, response) in\n        return self.items(from: response)\n      }\n      .onSuccess(context: self) { (self, items) in\n        self.present(items: items)\n      }\n      .onFailure(context: self) { (self, error) in\n        self.present(error: error)\n      }\n  }\n  \n  func items(from response: Response) throws -\u003e [Items] {\n    /* ... extract items from response ... */\n  }\n  \n  func present(items: [Items]) {\n    /* ... update UI ... */\n  }\n}\n\nclass MyService {\n  func perform(request: Request) -\u003e Future\u003cResponse\u003e {\n    /* ... */\n  }\n}\n```\n\n## In Depth\n\nLet's assume that we have:\n\n* `Person` is an example of a struct that contains information about the person.\n* `MyService` is an example of a class that serves as an entry point to the model. Works in a background.\n* `MyViewController` is an example of a class that manages UI-related instances. Works on the main queue.\n\n#### Code on callbacks\n\n```swift\nextension MyViewController {\n  func present(personWithID identifier: String) {\n    myService.fetch(personWithID: identifier) {\n      (person, error) in\n\n      /* do not forget to dispatch to the main queue */\n      DispatchQueue.main.async {\n\n        /* do not forget the [weak self] */\n        [weak self] in\n        guard let strongSelf = self\n          else { return }\n\n        if let person = person {\n          strongSelf.present(person: person)\n        } else if let error = error {\n          strongSelf.present(error: error)\n        } else {\n          fatalError(\"There is neither person nor error. What has happened to this world?\")\n        }\n      }\n    }\n  }\n}\n\nextension MyService {\n  func fetch(personWithID: String, callback: @escaping (Person?, Error?) -\u003e Void) {\n    /* ... */\n  }\n}\n```\n\n* \"do not forget\" comment **x2**\n* the block will be retained and called even if MyViewController was already deallocated\n\n#### Code with other libraries that provide futures\n\n```swift\nextension MyViewController {\n  func present(personWithID identifier: String) {\n    myService.fetch(personWithID: identifier)\n\n      /* do not forget to dispatch to the main queue */\n      .onComplete(executor: .main) {\n\n        /* do not forget the [weak self] */\n        [weak self] (completion) in\n        if let strongSelf = self {\n          completion.onSuccess(strongSelf.present(person:))\n          completion.onFailure(strongSelf.present(error:))\n        }\n      }\n  }\n}\n\nextension MyService {\n  func fetch(personWithID: String) -\u003e Future\u003cPerson\u003e {\n    /* ... */\n  }\n}\n```\n\n* \"do not forget\" comment **x2**\n* the block will be retained and called even if MyViewController was already deallocated\n\n#### Code with [AsyncNinja](https://github.com/AsyncNinja/AsyncNinja)\n\n```swift\nextension MyViewController {\n  func present(personWithID identifier: String) {\n    myService.fetch(personWithID: identifier)\n      .onSuccess(context: self) { (self, person) in\n        self.present(person: person)\n      }\n      .onFailure(context: self) { (self, error) in\n        self.present(error: error)\n      }\n  }\n}\n\nextension MyService {\n  func fetch(personWithID: String) -\u003e Future\u003cPerson\u003e {\n    /* ... */\n  }\n}\n```\n\n* \"do not forget\" comment **NONE**\n* the block will be retained and called as long as specified context (MyViewController) exists\n* [Want to see extended explanation?](https://medium.com/@AntonMironov/moving-to-nice-asynchronous-swift-code-7b0cb2eadde1)\n\n## Using Futures\n\nLet's assume that we have function that finds all prime numbers lesser than n\n\n```swift\nfunc primeNumbers(to n: Int) -\u003e [Int] { /* ... */ }\n```\n\n#### Making future\n\n```swift\nlet futurePrimeNumbers: Future\u003c[Int]\u003e = future { primeNumbers(to: 10_000_000) }\n```\n\n#### Applying transformation\n\n```swift\nlet futureSquaredPrimeNumbers = futurePrimeNumbers\n  .map { (primeNumbers) -\u003e [Int] in\n    return primeNumbers.map { (number) -\u003e Int\n      return number * number\n    }\n  }\n```\n\n#### Synchronously waiting for completion\n\n```swift\nif let fallibleNumbers = futurePrimeNumbers.wait(seconds: 1.0) {\n  print(\"Number of prime numbers is \\(fallibleNumbers.success?.count)\")\n} else {\n  print(\"Did not calculate prime numbers yet\")\n}\n```\n\n#### Subscribing for completion\n\n```swift\nfuturePrimeNumbers.onComplete { (falliblePrimeNumbers) in\n  print(\"Number of prime numbers is \\(falliblePrimeNumbers.success?.count)\")\n}\n```\n\n#### Combining futures\n\n```swift\nlet futureA: Future\u003cA\u003e = /* ... */\nlet futureB: Future\u003cB\u003e = /* ... */\nlet futureC: Future\u003cC\u003e = /* ... */\nlet futureABC: Future\u003c(A, B, C)\u003e = zip(futureA, futureB, futureC)\n```\n\n#### Transition from callbacks-based flow to futures-based flow:\n\n```swift\nclass MyService {\n  /* implementation */\n  \n  func fetchPerson(withID personID: Person.Identifier) -\u003e Future\u003cPerson\u003e {\n    let promise = Promise\u003cPerson\u003e()\n    self.fetchPerson(withID: personID, callback: promise.complete)\n    return promise\n  }\n}\n```\n\n#### Transition from futures-based flow to callbacks-based flow\n\n```swift\nclass MyService {\n  /* implementation */\n  \n  func fetchPerson(withID personID: Person.Identifier,\n                   callback: @escaping (Fallible\u003cPerson\u003e) -\u003e Void) {\n    self.fetchPerson(withID: personID)\n      .onComplete(callback)\n  }\n}\n```\n\n## Using Channels\nLet's assume we have function that returns channel of prime numbers: sends prime numbers as finds them and sends number of found numbers as completion\n\n```swift\nfunc makeChannelOfPrimeNumbers(to n: Int) -\u003e Channel\u003cInt, Int\u003e { /* ... */ }\n```\n\n#### Applying transformation\n\n```swift\nlet channelOfSquaredPrimeNumbers = channelOfPrimeNumbers\n  .map { (number) -\u003e Int in\n      return number * number\n    }\n```\n\n#### Synchronously iterating over update values.\n\n```swift\nfor number in channelOfPrimeNumbers {\n  print(number)\n}\n```\n\n#### Synchronously waiting for completion\n\n```swift\nif let fallibleNumberOfPrimes = channelOfPrimeNumbers.wait(seconds: 1.0) {\n  print(\"Number of prime numbers is \\(fallibleNumberOfPrimes.success)\")\n} else {\n  print(\"Did not calculate prime numbers yet\")\n}\n```\n\n#### Synchronously waiting for completion #2\n\n```swift\nlet (primeNumbers, numberOfPrimeNumbers) = channelOfPrimeNumbers.waitForAll()\n```\n\n#### Subscribing for update\n\n```swift\nchannelOfPrimeNumbers.onUpdate { print(\"Update: \\($0)\") }\n```\n\n#### Subscribing for completion\n\n```swift\nchannelOfPrimeNumbers.onComplete { print(\"Completed: \\($0)\") }\n```\n\n#### Making `Channel`\n\n```swift\nfunc makeChannelOfPrimeNumbers(to n: Int) -\u003e Channel\u003cInt, Int\u003e {\n  return channel { (update) -\u003e Int in\n    var numberOfPrimeNumbers = 0\n    var isPrime = Array(repeating: true, count: n)\n\n    for number in 2..\u003cn where isPrime[number] {\n      numberOfPrimeNumbers += 1\n      update(number)\n\n      // updating seive\n      var seiveNumber = number + number\n      while seiveNumber \u003c n {\n        isPrime[seiveNumber] = false\n        seiveNumber += number\n      }\n    }\n\n    return numberOfPrimeNumbers\n  }\n}\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAsyncNinja%2FAsyncNinja","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAsyncNinja%2FAsyncNinja","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAsyncNinja%2FAsyncNinja/lists"}