RxSwift is a reactive programming library for Swift that implements the Observable pattern. According to ReactiveX, 2025, it is the most popular implementation of Reactive Extensions for the Apple ecosystem. Observable serves as an event source, while Observer subscribes to receive events.
Key Takeaways
RxSwift is a reactive programming library for the Swift language, ported from Reactive Extensions (Rx). It allows describing asynchronous and event-driven programs through Observable Sequence — a sequence of data available over time. iOS developers use RxSwift to handle network requests, UI events, and streaming data without nested callbacks.
The foundation of RxSwift consists of two key protocols: ObservableType — an event source that can emit three types of signals: .next(value), .error(error), and .completed. Observer subscribes to Observable via the subscribe method and receives these events. This model is called Reactive Streams and guarantees that no event is lost with a proper subscription.
RxSwift provides over 300 operators for working with streams: map transforms each event, filter passes only matching events, flatMap unfolds nested Observables into a single stream. Operators are chained together, creating a declarative data processing pipeline without side effects.
In addition to the basic Observable, RxSwift provides specialized wrapper types. Single emits exactly one value or an error — ideal for HTTP requests. Completable completes with success or failure without a value — for write operations. Maybe combines both scenarios: it can complete with a value, without a value, or with an error. These Traits simplify semantics and make code self-documenting.
Reactive programming in RxSwift is built on the Observer pattern. ObservableSequence is analogous to Sequence from the standard library, but with asynchronous access to elements. The event stream is passed through a chain of operators, each returning a new ObservableSequence without mutating the original.
Operators in RxSwift are pure functions that take one ObservableSequence and return a new one. For example, map creates a new sequence by applying a transformation to each element. By combining operators, the developer builds a pipeline where data goes through all processing stages without intermediate variables.
Schedulers are an abstraction over execution threads in RxSwift. A Scheduler determines on which thread the code will execute: MainScheduler — the UI thread, SerialDispatchQueueScheduler — a background queue. The operators subscribeOn and observeOn specify where work is performed and where results are processed, respectively.
RxSwift includes several basic types, each solving its own task in the reactive pipeline. Single is an Observable that emits exactly one value or an error, convenient for network requests. Completable completes with success or failure without a value. Maybe combines the properties of Single and Completable.
Subject is a hot Observable that also acts as an Observer. PublishSubject emits only new events to subscribers, BehaviorSubject — the last event plus new ones. Relay is a variant of Subject that does not emit .error or .completed, guaranteeing stream continuity. BehaviorRelay stores the current value and is suitable for State-driven UI.
DisposeBag is a collection of Disposables that automatically unsubscribes all subscriptions upon its deallocation. In iOS, DisposeBag is typically added to UIViewController or UIView. When the screen is closed, DisposeBag is cleared — this prevents memory leaks and access to non-existent UI elements.
Below is an example of creating an Observable from an array of data using the map operator to transform strings:
let observable = Observable.of("Swift", "RxSwift", "Reactive")
observable
.map { $0.uppercased() }
.subscribe(onNext: { value in
print("Received: \(value)")
})
.disposed(by: disposeBag)
The second example demonstrates combining two network requests using zip — the operator waits until both Observables emit a value and combines the results into a tuple:
let first = fetchUser(id: "123")
let second = fetchPosts(userId: "123")
Observable
.zip(first, second)
.observe(on: MainScheduler.instance)
.subscribe(onNext: { user, posts in
updateUI(user: user, posts: posts)
})
.disposed(by: disposeBag)
The third example shows using BehaviorRelay to store state and automatically update the UI on changes: each state.accept() change immediately broadcasts to subscribers, which is ideal for State patterns in MVVM architecture.
let state = BehaviorRelay(value: "idle")
state
.subscribe(onNext: { status in
print("Status: \(status)")
})
.disposed(by: disposeBag)
state.accept("loading") // Prints: Status: loading
RxSwift differs from traditional asynchronous methods (Delegation, NotificationCenter, Callback) in its declarative nature and composability. Unlike Combine, RxSwift supports iOS 9+ and has more operators. However, Combine is integrated into Foundation and SwiftUI at the language level, giving it an advantage in new Apple projects.
The main advantage of RxSwift over GCD (Grand Central Dispatch) is the ability to combine and transform data streams at the abstraction level rather than managing queues manually. However, RxSwift requires learning reactive concepts, which increases the entry threshold for the team.
In practice, RxSwift is used in large projects where reactive chains connect UI events, business logic, and network interaction in a single pipeline. For example, in trading applications, quote streams are processed via RxSwift: ticks arrive from WebSocket, go through filtering, are grouped by timeframe, and displayed on a chart in real time. Such a scenario is difficult to implement via Delegation or NotificationCenter without losing readability.
Alternatives to RxSwift include Combine (iOS 13+), AsyncSequence from Swift Concurrency (iOS 15+), as well as third-party libraries like ReactiveSwift without ties to Apple platforms. The choice depends on the minimum supported iOS version and developer experience. For new iOS 15+ projects, teams often choose AsyncSequence — it does not require library installation and uses native Swift language constructs.
Architectural patterns with RxSwift typically follow MVVM or Clean Architecture. ViewModel contains all business logic as Observable chains, and View subscribes to transformed data. The Input-Output pattern separates input events (taps, text input) from output states (button text, loader visibility). This approach simplifies testing: ViewModel is tested without UI through virtual Schedulers.
RxSwift is suitable for both small projects with a few screens and large enterprise applications with dozens of modules. In large projects, reactive chains permeate the entire architecture: from UserDefaults observation via RxProperty to network requests through Moya (an RxSwift wrapper over Alamofire). Each module is isolated and communicates via reactive interfaces, which simplifies implementation replacement without changing subscribers. With proper architecture, RxSwift reduces the amount of code compared to classical approaches, since there is no need to write boilerplate for KVO, Target-Action, or NotificationCenter.
RxSwift is actively used in projects with RxDataSources — a library for reactive work with UITableView and UICollectionView. RxDataSources automatically computes the difference between old and new sets of cells and applies animated changes. This relieves the developer from manual work with beginUpdates/endUpdates and eliminates crashes due to data inconsistency.
For debugging RxSwift chains, there is the debug() operator — it logs all events: subscribe, next, error, completed, dispose. This is an indispensable tool when developing complex reactive pipelines. debug(String) takes an identifier that appears in the logs. For memory profiling, RxSwift.Resources.total shows the total number of active Observables and Disposables in the application — helps identify leaks when DisposeBag is not cleared or a retain cycle holds a subscription. The additional operator takeUntil(self.rx.deallocated) automatically cancels the subscription upon object deallocation — this is another layer of leak protection.
When writing RxSwift code, it is important to follow the principle of single Observable per subscription: each ViewController should not create more than one Subscription to the same Observable — this reduces the risk of race conditions. For memoization of Observables, the share() operator is used, which turns a cold Observable into a hot one with a replay buffer of size 1. When working with shared resources, use connect() to control the start of emission — this guarantees that all subscribers connect before the first event.
Testing RxSwift code is done through TestScheduler — a virtual Scheduler that allows managing time. testScheduler.createHotObservable(values) creates an Observable with a predefined sequence of events by virtual time. testScheduler.start() starts processing. TestScheduler allows checking in what order and at what virtual timestamp events occur, without real delays, making tests fast and deterministic.
Frequently Asked Questions
Observable is a cold source: it does not emit events until subscription. Subject is hot: it emits events regardless of subscribers and allows manual insertion of values via onNext. PublishSubject passes only new events, BehaviorSubject — the last one plus new ones.
DisposeBag stores all Disposable subscriptions. When DisposeBag is deallocated (for example, when a ViewController is closed), all stored subscriptions are automatically canceled. This guarantees that Observable will not send an event to a destroyed UI object.
If the minimum iOS version is 13+ and the team knows SwiftUI — choose Combine. If the project supports iOS 12 and below or requires a larger set of operators — RxSwift. Combine offers better integration with Foundation (URLSession, Timer, NotificationCenter).
Schedulers abstract execution threads. subscribeOn specifies on which Scheduler the subscription is performed (usually background). observeOn determines on which Scheduler events are received (most often MainScheduler for UI updates). SerialDispatchQueueScheduler works through GCD.
Yes, RxSwift can be integrated with SwiftUI via ObservableObject. Use BehaviorRelay as @Published properties: subscription to Relay broadcasts changes to Combine, and SwiftUI redraws the View through @ObservedObject. This is a popular migration pattern from UIKit to SwiftUI.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also