Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/AsyncNinja/AsyncNinja
A complete set of primitives for concurrency and reactive programming on Swift
https://github.com/AsyncNinja/AsyncNinja
async channel concurrency concurrency-library functional future reactive swift
Last synced: about 1 month ago
JSON representation
A complete set of primitives for concurrency and reactive programming on Swift
- Host: GitHub
- URL: https://github.com/AsyncNinja/AsyncNinja
- Owner: AsyncNinja
- License: mit
- Created: 2016-08-07T23:29:44.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2021-10-03T20:19:59.000Z (over 3 years ago)
- Last Synced: 2024-11-07T19:20:31.487Z (2 months ago)
- Topics: async, channel, concurrency, concurrency-library, functional, future, reactive, swift
- Language: Swift
- Homepage: https://async.ninja/
- Size: 2.05 MB
- Stars: 156
- Watchers: 6
- Forks: 16
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.MD
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
- awesome-ios - AsyncNinja - A complete set of concurrency and reactive programming primitives. (Concurrency / Linter)
- awesome-swift - AsyncNinja - A complete set of concurrency and reactive programming primitives. (Libs / Concurrency)
- awesome-swift - AsyncNinja - A complete set of concurrency and reactive programming primitives. (Libs / Concurrency)
- fucking-awesome-swift - AsyncNinja - A complete set of concurrency and reactive programming primitives. (Libs / Concurrency)
- awesome-ios-star - AsyncNinja - A complete set of concurrency and reactive programming primitives. (Concurrency / Linter)
- awesome-system-swift - AsyncNinja
- fucking-awesome-ios - AsyncNinja - A complete set of concurrency and reactive programming primitives. (Concurrency / Linter)
- fucking-awesome-ios - AsyncNinja - A complete set of concurrency and reactive programming primitives. (Concurrency / Linter)
- awesome-swift - AsyncNinja - A complete set of primitives for concurrency and reactive programming on Swift ` π a year ago ` (Concurrency [π](#readme))
README
# ![AsyncNinja Title](title_image.png)
## A complete set of primitives for concurrency and reactive programming on Swift[![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/AsyncNinja/Lobby)
[![CocoaPods](https://img.shields.io/cocoapods/v/AsyncNinja.svg)](https://cocoapods.org/pods/AsyncNinja)
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![Build Status](https://travis-ci.org/AsyncNinja/AsyncNinja.svg?branch=master)](https://travis-ci.org/AsyncNinja)* **1.4.0 is the latest and greatest, but only for Swift 4.2 and 5.0**
* **use 1.3.0 is for Swift 4.0+**
* **use 1.2.4 for latest release for Swift 3**| | Features |
|---|---|
| π¦
powerful primitives | `Future`, `Promise`, `Channel`, `Producer`, `Sink`, `Cache`, ... |
| π€
versatile transformations | `map`, `filter`, `recover`, `debounce`, `distinct`, ... |
| βοΈ
convenient combination | `flatMap`, `merge`, `zip`, `sample`, `scan`, `reduce`, ... |
| π
improves existing things | Key-Value Observing, target-action, notifications, bindings |
| π³
less boilerplate code | neat cancellation, threading, memory manament |
| πΆ
extendable | powerful extensions for `URLSession`, UI controls, `CoreData`, ... |
| π±
all platforms
| π₯ macOS 10.10+ π± iOS 8.0+ πΊ tvOS 9.0+ βοΈ watchOS 2.0+ π§ Linux |
| π€
documentation | 100% + sample code, **[see full documentation](http://docs.async.ninja/)** |
| π©
simple integration | [SPM](Documentation/Integration.md#using-swift-package-manager), [CocoaPods](Documentation/Integration.md#cocoapods), [Carthage](Documentation/Integration.md#Ρarthage) |* Related articles
* 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)
* [Known users](known_users.md)## Communication
* [GitHub issues](https://github.com/AsyncNinja/AsyncNinja/issues/new)
* [Gitter.im](https://gitter.im/AsyncNinja/Lobby)
* [Considering contribution?](CONTRIBUTING.md)## Reactive Programming
#### reactive properties
```swift
let searchResults = searchBar.rp.text
.debounce(interval: 0.3)
.distinct()
.flatMap(behavior: .keepLatestTransform) { (query) -> Future<[SearchResult]> in
return query.isEmpty
? .just([])
: searchGitHub(query: query).recover([])
}
```#### bindings
- unbinds automatically
- dispatches to a correct queue automatically
- no `.observeOn(MainScheduler.instance)` and `.disposed(by: disposeBag)````swift
class MyViewController: UIViewController {
/* ... */
@IBOutlet weak var myLabel: UILabel!override func viewDidLoad() {
super.viewDidLoad()
UIDevice.current.rp.orientation
.map { $0.description }
.bind(myLabel.rp.text)
}
/* ... */
}
```#### contexts usage
- no `[weak self]`
- no `DispatchQueue.main.async { ... }`
- no `.observeOn(MainScheduler.instance)````swift
class MyViewController: NSViewController {
let service: MyService/* ... */
func fetchAndPresentItems(for request: Request) {
service.perform(request: request)
.map(context: self, executor: .primary) { (self, response) in
return self.items(from: response)
}
.onSuccess(context: self) { (self, items) in
self.present(items: items)
}
.onFailure(context: self) { (self, error) in
self.present(error: error)
}
}
func items(from response: Response) throws -> [Items] {
/* ... extract items from response ... */
}
func present(items: [Items]) {
/* ... update UI ... */
}
}class MyService {
func perform(request: Request) -> Future {
/* ... */
}
}
```## In Depth
Let's assume that we have:
* `Person` is an example of a struct that contains information about the person.
* `MyService` is an example of a class that serves as an entry point to the model. Works in a background.
* `MyViewController` is an example of a class that manages UI-related instances. Works on the main queue.#### Code on callbacks
```swift
extension MyViewController {
func present(personWithID identifier: String) {
myService.fetch(personWithID: identifier) {
(person, error) in/* do not forget to dispatch to the main queue */
DispatchQueue.main.async {/* do not forget the [weak self] */
[weak self] in
guard let strongSelf = self
else { return }if let person = person {
strongSelf.present(person: person)
} else if let error = error {
strongSelf.present(error: error)
} else {
fatalError("There is neither person nor error. What has happened to this world?")
}
}
}
}
}extension MyService {
func fetch(personWithID: String, callback: @escaping (Person?, Error?) -> Void) {
/* ... */
}
}
```* "do not forget" comment **x2**
* the block will be retained and called even if MyViewController was already deallocated#### Code with other libraries that provide futures
```swift
extension MyViewController {
func present(personWithID identifier: String) {
myService.fetch(personWithID: identifier)/* do not forget to dispatch to the main queue */
.onComplete(executor: .main) {/* do not forget the [weak self] */
[weak self] (completion) in
if let strongSelf = self {
completion.onSuccess(strongSelf.present(person:))
completion.onFailure(strongSelf.present(error:))
}
}
}
}extension MyService {
func fetch(personWithID: String) -> Future {
/* ... */
}
}
```* "do not forget" comment **x2**
* the block will be retained and called even if MyViewController was already deallocated#### Code with [AsyncNinja](https://github.com/AsyncNinja/AsyncNinja)
```swift
extension MyViewController {
func present(personWithID identifier: String) {
myService.fetch(personWithID: identifier)
.onSuccess(context: self) { (self, person) in
self.present(person: person)
}
.onFailure(context: self) { (self, error) in
self.present(error: error)
}
}
}extension MyService {
func fetch(personWithID: String) -> Future {
/* ... */
}
}
```* "do not forget" comment **NONE**
* the block will be retained and called as long as specified context (MyViewController) exists
* [Want to see extended explanation?](https://medium.com/@AntonMironov/moving-to-nice-asynchronous-swift-code-7b0cb2eadde1)## Using Futures
Let's assume that we have function that finds all prime numbers lesser than n
```swift
func primeNumbers(to n: Int) -> [Int] { /* ... */ }
```#### Making future
```swift
let futurePrimeNumbers: Future<[Int]> = future { primeNumbers(to: 10_000_000) }
```#### Applying transformation
```swift
let futureSquaredPrimeNumbers = futurePrimeNumbers
.map { (primeNumbers) -> [Int] in
return primeNumbers.map { (number) -> Int
return number * number
}
}
```#### Synchronously waiting for completion
```swift
if let fallibleNumbers = futurePrimeNumbers.wait(seconds: 1.0) {
print("Number of prime numbers is \(fallibleNumbers.success?.count)")
} else {
print("Did not calculate prime numbers yet")
}
```#### Subscribing for completion
```swift
futurePrimeNumbers.onComplete { (falliblePrimeNumbers) in
print("Number of prime numbers is \(falliblePrimeNumbers.success?.count)")
}
```#### Combining futures
```swift
let futureA: Future = /* ... */
let futureB: Future = /* ... */
let futureC: Future = /* ... */
let futureABC: Future<(A, B, C)> = zip(futureA, futureB, futureC)
```#### Transition from callbacks-based flow to futures-based flow:
```swift
class MyService {
/* implementation */
func fetchPerson(withID personID: Person.Identifier) -> Future {
let promise = Promise()
self.fetchPerson(withID: personID, callback: promise.complete)
return promise
}
}
```#### Transition from futures-based flow to callbacks-based flow
```swift
class MyService {
/* implementation */
func fetchPerson(withID personID: Person.Identifier,
callback: @escaping (Fallible) -> Void) {
self.fetchPerson(withID: personID)
.onComplete(callback)
}
}
```## Using Channels
Let'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```swift
func makeChannelOfPrimeNumbers(to n: Int) -> Channel { /* ... */ }
```#### Applying transformation
```swift
let channelOfSquaredPrimeNumbers = channelOfPrimeNumbers
.map { (number) -> Int in
return number * number
}
```#### Synchronously iterating over update values.
```swift
for number in channelOfPrimeNumbers {
print(number)
}
```#### Synchronously waiting for completion
```swift
if let fallibleNumberOfPrimes = channelOfPrimeNumbers.wait(seconds: 1.0) {
print("Number of prime numbers is \(fallibleNumberOfPrimes.success)")
} else {
print("Did not calculate prime numbers yet")
}
```#### Synchronously waiting for completion #2
```swift
let (primeNumbers, numberOfPrimeNumbers) = channelOfPrimeNumbers.waitForAll()
```#### Subscribing for update
```swift
channelOfPrimeNumbers.onUpdate { print("Update: \($0)") }
```#### Subscribing for completion
```swift
channelOfPrimeNumbers.onComplete { print("Completed: \($0)") }
```#### Making `Channel`
```swift
func makeChannelOfPrimeNumbers(to n: Int) -> Channel {
return channel { (update) -> Int in
var numberOfPrimeNumbers = 0
var isPrime = Array(repeating: true, count: n)for number in 2..