RxSwift — what it is, features and reactive programming

Author: IT Sectr Published: 2026-05-03 Reading time: 10 min

RxSwift is a reactive programming library for iOS that implements the Observable pattern and functional operators for working with asynchronous data streams. It ports ReactiveX (Rx) concepts to Swift, providing a unified API for handling events from UI, network requests, databases and timers. According to ReactiveX, 2025, the library is used in more than 60,000 projects on GitHub and remains the standard for reactive programming in Swift until the advent of native Combine. RxSwift replaces delegates, closures and notifications with a single event processing chain.

Key Takeaways

  • RxSwift — ReactiveX implementation for Swift based on the Observable pattern and operator chains
  • Observable — a source of asynchronous events that can complete successfully, with an error, or infinitely
  • Subject — a hybrid of Observable and Observer that allows manually sending events to the stream
  • Schedulers control on which thread subscription, observation and event processing are performed
  • Operators map, flatMap, filter, combineLatest transform and combine streams without race conditions

What is RxSwift?

RxSwift is the Swift port of the ReactiveX (Rx) library created by Microsoft for .NET in 2012. The core idea of Rx is to represent any asynchronous data source as an Observable sequence to which functional operators can be applied. In iOS development, RxSwift is used to bind UI events (button taps, text input, gestures) to application logic without explicit delegates and target-action.

The library consists of three modules: RxSwift (core — Observable, Operator, Scheduler), RxCocoa (UIKit integration — rx extensions for UIButton, UITextField, UITableView) and RxRelay (Subject without terminal events). This separation allows using the core in server-side Swift and tests, connecting the UI wrapper only for iOS applications.

According to the Stack Overflow, 2025 survey, RxSwift is among the top 5 most used third-party libraries for iOS. The main reason for its popularity is the uniform handling of all asynchronous patterns: network requests via URLSession, animations via UIViewPropertyAnimator, notifications via NotificationCenter and delegates via DelegateProxy — all of this boils down to an Observable with a predictable lifecycle.

Reactive Programming Philosophy

Reactive programming is a paradigm in which the program reacts to data changes rather than polling for them. Instead of writing “get data → process → update UI”, the developer describes a transformation chain: “when data changes, apply filter, then map, then update UI”. RxSwift implements this paradigm through Observable chains with lazy execution — nothing happens until a subscriber appears.

Key RxSwift Components: Observable, Subject, Disposable

Observable is the fundamental RxSwift type representing a sequence of events over time. Observable can send three types of events: next (new value), error (error terminating the stream) and completed (successful completion). There are finite Observable (complete after sending all values) and infinite Observable (e.g. UI events — never complete).

Observable and Its Lifecycle

The lifecycle of an Observable includes three stages: creation (create, just, from), transformation (operators map, filter, flatMap) and subscription (subscribe). Without a subscription, Observable does not perform any actions — it is a lazy sequence. After subscription, Observable starts sending events to the subscriber until it completes or the subscriber cancels the subscription via Disposable.

swift
import RxSwift

// Creating Observable from array
let numbers = Observable.from([1, 2, 3, 4, 5])

// Transformation through operators
let squared = numbers
    .filter { $0 % 2 == 0 }
    .map { $0 * $0 }

// Subscription with event handling
let disposable = squared
    .subscribe(onNext: { print($0) },
               onError: { print("Error: \($0)") },
               onCompleted: { print("Done") })

Subject is a type that is both an Observable (can be subscribed to) and an Observer (can send events to). RxSwift provides four types of Subject: PublishSubject (only new events), BehaviorSubject (with an initial value), ReplaySubject (buffers the last N events) and AsyncSubject (only the last value before completion). Subject is useful for integrating imperative code (closures, delegates) into a reactive chain.

Disposable is a subscription cancellation token. When the subscriber no longer wants to receive events, it calls disposable.dispose(). In practice, DisposeBag is used: a collection of Disposable that automatically cancels all subscriptions upon deinitialization of the owner (e.g. UIViewController). This prevents memory leaks that are inevitable with manual subscription management.

Subject TypeInitial ValueReplays to SubscriberUse Case
PublishSubjectNoOnly new eventsUI events, rare notifications
BehaviorSubjectYesLast + newState, data stream
ReplaySubjectNoBuffer N + newHistory caching
AsyncSubjectNoOnly lastComputational tasks
PublishRelayNoOnly newWithout error/completed

Stream Transformation and Filtering Operators

Operators in RxSwift are functions that take one Observable and return another Observable, transforming the data stream. By combining operators, the developer builds declarative processing chains without intermediate variables and race conditions. All operators are lazy: the chain is built during description and executed upon subscription.

Transformation operators change each event in the stream. map applies a function to each element, flatMap unwraps Observable from each element into a single flat stream, scan accumulates intermediate results (similar to reduce but emitting each step). buffer groups elements by time or count, window divides the stream into nested Observable windows.

swift
// Example of operator chain: search with debounce
searchTextField.rx.text
    .orEmpty
    .debounce(.milliseconds(300), scheduler: MainScheduler.instance)
    .distinctUntilChanged()
    .flatMapLatest { query -> Observable<[String]> in
        return apiService.search(query)
    }
    .bind(to: tableView.rx.items(cellIdentifier: "Cell")) { _, item, cell in
        cell.textLabel?.text = item
    }
    .disposed(by: disposeBag)

Filtering operators only pass events that satisfy a condition. filter filters by predicate, distinctUntilChanged only passes values different from the previous one, take and takeWhile limit the number of events. skip and skipWhile skip the first N events or events until a condition is met. For error handling, catchError (intercept and substitute) and retry (retry sequence on error) are used.

Combination operators combine multiple Observables into one. combineLatest merges the latest values from multiple streams into a tuple, zip pairs elements with the same indices, merge combines multiple streams into one in the order of event arrival. withLatestFrom combines an event from the main stream with the latest value from another stream — useful for UI events with current state.

CategoryOperatorDescription
TransformationmapTransforms each value through a function
TransformationflatMapUnwraps nested Observables into a flat stream
FilteringfilterPasses values by predicate
FilteringdistinctUntilChangedPasses only changed values
CombinationcombineLatestCombines latest values from 2+ streams
CombinationzipPairs elements by index
ControldebounceDelays events until a pause in the stream
ControltakeTakes first N events and completes the stream

Schedulers and Thread Management

Scheduler in RxSwift is an abstraction over the execution thread (or queue). Scheduler determines on which thread event creation, transformation and subscription are executed. Unlike GCD, where the developer explicitly specifies DispatchQueue, RxSwift uses two parameters: subscribeOn (on which thread the Observable executes) and observeOn (on which thread the subscriber is called).

MainScheduler and BackgroundScheduler

MainScheduler executes code on the main thread — required for UIKit updates (all UI changes must happen on the main thread). SerialDispatchQueueScheduler wraps a serial GCD queue, ConcurrentDispatchQueueScheduler wraps a concurrent one. For background tasks, ConcurrentDispatchQueueScheduler with qos: .background or .utility is used.

swift
// Typical pattern: background work + UI on main
apiService.fetchData()
    .subscribeOn(ConcurrentDispatchQueueScheduler(qos: .background))
    .observeOn(MainScheduler.instance)
    .subscribe(onNext: { data in
        self.tableView.reloadData()
        self.loadingIndicator.stopAnimating()
    })
    .disposed(by: disposeBag)

Proper use of Scheduler prevents UI blocking during network requests and heavy computations. subscribeOn affects the entire upstream (all operators before observeOn execute on the specified scheduler). observeOn switches the downstream — all operators after observeOn execute on the specified scheduler. A chain can have multiple observeOn for switching between threads at different transformation stages.

SerialDispatchQueueScheduler guarantees sequential event processing, which is important for thread safety of shared resources. For high-load operations (image processing, JSON parsing) use ConcurrentDispatchQueueScheduler — RxSwift will preserve event order within one Observable, but different Observables may be processed in parallel.

Practical Application of RxSwift in iOS

RxSwift in iOS projects is most often used for binding UI with data through reactive chains. RxCocoa provides rx extensions for all standard UIKit components: rx.tap for UIButton, rx.text for UITextField, rx.selectedRow for UIPickerView. This allows abandoning @IBAction and delegates, replacing them with declarative subscriptions.

RxSwift with MVVM

MVVM (Model-View-ViewModel) is an architectural pattern in which ViewModel manages state and business logic, and View subscribes to reactive properties of the ViewModel. RxSwift fits perfectly with MVVM: ViewModel publishes Observable or Driver for data, View subscribes to them via bind. ViewModel has no reference to View — it returns Observable that View consumes.

swift
class LoginViewModel {
    let email = BehaviorRelay<String>(value: "")
    let password = BehaviorRelay<String>(value: "")

    var isFormValid: Observable<Bool> {
        return Observable
            .combineLatest(email, password) {
                !$0.isEmpty && $0.contains("@") && $1.count >= 6
            }
    }
}

// In ViewController:
viewModel.isFormValid
    .bind(to: loginButton.rx.isEnabled)
    .disposed(by: disposeBag)

Network requests are the second most common use case for RxSwift. URLSession.rx.response wraps an HTTP request into an Observable that returns (response, data) on success or error on failure. In combination with Codable and the map operator, a compact reactive API layer is obtained: Observable<MyModel> without callbacks and error handling in each method. On network error, the retry(3) operator automatically retries the request three times with exponential backoff.

RxSwift vs Combine

Combine is Apple’s native framework introduced in iOS 13. It solves the same tasks as RxSwift: working with asynchronous events through Publisher, Subscriber and operators. However, Combine has key differences: strict error typing via the Failure type, built-in Swift Concurrency support (async/await) and integration with SwiftUI through @Published and ObservableObject.

RxSwift wins in backward compatibility — it supports iOS 8+, while Combine requires iOS 13+. RxSwift has a richer ecosystem of third-party extensions (RxDataSources, RxGesture, RxAnimated) and detailed operator documentation. For projects supporting older iOS versions, RxSwift remains the only choice.

For new projects with iOS 13+, Apple recommends Combine. It is tightly integrated with SwiftUI, has a smaller binary size and official support. However, migrating existing RxSwift code to Combine requires rewriting all Observable → Publisher, and the RxCocoa ecosystem does not have a full Combine equivalent — UIKit does not have native Publisher for all UI components.

FeatureRxSwiftCombine
Minimum iOSiOS 8+iOS 13+
Error TypeError (any)Generic Failure
UI ExtensionsRxCocoa (UIKit, AppKit)@Published (SwiftUI)
Operators400+ operators~100 operators
Swift ConcurrencyVia bridgeNative support
StatusThird-partyApple official

Frequently Asked Questions

What is the difference between Observable and Subject in RxSwift?

Observable is a source of events with lazy execution. Subject is both an Observable and an Observer — you can subscribe to it and send new events to it manually. Subject is useful for integrating imperative code into reactive chains.

When to use RxSwift instead of Combine?

RxSwift is chosen when iOS 11-12 support is needed, when there is an existing RxSwift codebase, or when a rich operator ecosystem is required (400+ vs ~100 in Combine). For new projects on iOS 13+, Combine is preferred.

What is DisposeBag and why is it needed?

DisposeBag is a collection of Disposable tokens that automatically cancels all subscriptions upon owner deinitialization. Without DisposeBag, the subscription creates a strong reference to the closure, leading to memory leaks when UIViewController is destroyed.

How to handle errors in RxSwift?

Use catchError to replace an error with a default value, retry to retry Observable execution, materialize to transform the error into an event. In the UI layer, Driver and Signal do not pass errors — they are handled internally.

How is Driver different from Observable in RxCocoa?

Driver is a special type of Observable that guarantees execution on MainScheduler, absence of errors and resource sharing. Driver ensures that UI updates happen on the main thread. Observable does not provide such guarantees — observeOn is required.

Summary

  • RxSwift is a reactive programming library for iOS implementing the Observable pattern and 400+ transformation operators
  • Observable, Subject, Disposable are three fundamental types forming the basis of all reactive chains
  • Operators map, flatMap, filter, combineLatest, debounce allow building declarative chains without race conditions
  • Schedulers (subscribeOn / observeOn) manage execution threads, preventing UI blocking
  • RxCocoa provides rx extensions for UIKit, replacing delegates and target-action with reactive subscriptions
  • Combine is Apple’s native alternative for iOS 13+, but RxSwift remains relevant for older versions

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.

Discuss the project

Read also