- July 26, 2025
- Mins Read
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 |
🔩 simple integration |
SPM, CocoaPods, Carthage |
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([])
}
.observeOn(MainScheduler.instance)
and .disposed(by: disposeBag)
class MyViewController: UIViewController {
/* … */
@IBOutlet weak var myLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
UIDevice.current.rp.orientation
.map { $0.description }
.bind(myLabel.rp.text)
}
/* … */
}
[weak self]
DispatchQueue.main.async { ... }
.observeOn(MainScheduler.instance)
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<Response> {
/* … */
}
}
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.
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) {
/* … */
}
}
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<Person> {
/* … */
}
}
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<Person> {
/* … */
}
}
Let’s assume that we have function that finds all prime numbers lesser than n
func primeNumbers(to n: Int) -> [Int] { /* … */ }
let futurePrimeNumbers: Future<[Int]> = future { primeNumbers(to: 10_000_000) }
let futureSquaredPrimeNumbers = futurePrimeNumbers
.map { (primeNumbers) -> [Int] in
return primeNumbers.map { (number) -> Int
return number * number
}
}
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”)
}
futurePrimeNumbers.onComplete { (falliblePrimeNumbers) in
print(“Number of prime numbers is \(falliblePrimeNumbers.success?.count)”)
}
let futureA: Future<A> = /* … */
let futureB: Future<B> = /* … */
let futureC: Future<C> = /* … */
let futureABC: Future<(A, B, C)> = zip(futureA, futureB, futureC)
class MyService {
/* implementation */
func fetchPerson(withID personID: Person.Identifier) -> Future<Person> {
let promise = Promise<Person>()
self.fetchPerson(withID: personID, callback: promise.complete)
return promise
}
}
class MyService {
/* implementation */
func fetchPerson(withID personID: Person.Identifier,
callback: @escaping (Fallible<Person>) -> Void) {
self.fetchPerson(withID: personID)
.onComplete(callback)
}
}
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
func makeChannelOfPrimeNumbers(to n: Int) -> Channel<Int, Int> { /* … */ }
let channelOfSquaredPrimeNumbers = channelOfPrimeNumbers
.map { (number) -> Int in
return number * number
}
for number in channelOfPrimeNumbers {
print(number)
}
if let fallibleNumberOfPrimes = channelOfPrimeNumbers.wait(seconds: 1.0) {
print(“Number of prime numbers is \(fallibleNumberOfPrimes.success)”)
} else {
print(“Did not calculate prime numbers yet”)
}
let (primeNumbers, numberOfPrimeNumbers) = channelOfPrimeNumbers.waitForAll()
channelOfPrimeNumbers.onUpdate { print(“Update: \($0)”) }
channelOfPrimeNumbers.onComplete { print(“Completed: \($0)”) }
Channel
func makeChannelOfPrimeNumbers(to n: Int) -> Channel<Int, Int> {
return channel { (update) -> Int in
var numberOfPrimeNumbers = 0
var isPrime = Array(repeating: true, count: n)
for number in 2..<n where isPrime[number] {
numberOfPrimeNumbers += 1
update(number)
// updating seive
var seiveNumber = number + number
while seiveNumber < n {
isPrime[seiveNumber] = false
seiveNumber += number
}
}
return numberOfPrimeNumbers
}
}
NavigationKit is a lightweight library which makes SwiftUI navigation super easy to use. 💻 Installation 📦 Swift Package Manager Using Swift Package Manager, add ...
An alternative SwiftUI NavigationView implementing classic stack-based navigation giving also some more control on animations and programmatic navigation. NavigationStack Installation ...
With SwiftUI Router you can power your SwiftUI app with path-based routing. By utilizing a path-based system, navigation in your app becomes ...
This package takes SwiftUI's familiar and powerful NavigationStack API and gives it superpowers, allowing you to use the same API not just ...