RxSwift là một thư viện lập trình phản ứng dành cho iOS, triển khai mẫu Observable và các toán tử chức năng để làm việc với luồng dữ liệu bất đồng bộ. Nó chuyển các khái niệm ReactiveX (Rx) sang ngôn ngữ Swift, cung cấp một API thống nhất để xử lý các sự kiện từ UI, yêu cầu mạng, cơ sở dữ liệu và bộ hẹn giờ. Theo ReactiveX, 2025, thư viện được sử dụng trong hơn 60.000 dự án trên GitHub và vẫn là tiêu chuẩn lập trình phản ứng cho Swift cho đến khi Combine gốc ra đời. RxSwift thay thế delegate, closure và thông báo bằng một chuỗi xử lý sự kiện duy nhất.
Những điểm chính
RxSwift là bản port Swift của thư viện ReactiveX (Rx) do Microsoft tạo ra cho .NET vào năm 2012. Ý tưởng cốt lõi của Rx là biểu diễn bất kỳ nguồn dữ liệu bất đồng bộ nào dưới dạng chuỗi Observable mà các toán tử chức năng có thể được áp dụng. Trong phát triển iOS, RxSwift được sử dụng để liên kết các sự kiện UI (nhấn nút, nhập văn bản, cử chỉ) với logic ứng dụng mà không cần delegate rõ ràng và target-action.
Thư viện bao gồm ba mô-đun: RxSwift (lõi — Observable, Operator, Scheduler), RxCocoa (tích hợp UIKit — phần mở rộng rx cho UIButton, UITextField, UITableView) và RxRelay (Subject không có sự kiện kết thúc). Sự phân tách này cho phép sử dụng lõi trong Swift phía máy chủ và kiểm tra, chỉ kết nối trình bao bọc UI cho các ứng dụng iOS.
Theo khảo sát Stack Overflow, 2025, RxSwift nằm trong top 5 thư viện bên thứ ba được sử dụng nhiều nhất cho iOS. Lý do chính cho sự phổ biến của nó là xử lý thống nhất tất cả các mẫu bất đồng bộ: yêu cầu mạng qua URLSession, hoạt ảnh qua UIViewPropertyAnimator, thông báo qua NotificationCenter và delegate qua DelegateProxy — tất cả đều quy về một Observable với vòng đời có thể dự đoán được.
Lập trình phản ứng là một mô hình trong đó chương trình phản ứng với các thay đổi dữ liệu thay vì thăm dò chúng. Thay vì viết “lấy dữ liệu → xử lý → cập nhật UI”, nhà phát triển mô tả một chuỗi biến đổi: “khi dữ liệu thay đổi, áp dụng bộ lọc, sau đó map, sau đó cập nhật UI”. RxSwift triển khai mô hình này thông qua chuỗi Observable với thực thi lười biếng — không có gì xảy ra cho đến khi có người đăng ký xuất hiện.
Observable là kiểu cơ bản của RxSwift đại diện cho một chuỗi các sự kiện theo thời gian. Observable có thể gửi ba loại sự kiện: next (giá trị mới), error (lỗi kết thúc luồng) và completed (hoàn thành thành công). Có Observable hữu hạn (hoàn thành sau khi gửi tất cả giá trị) và Observable vô hạn (ví dụ: sự kiện UI — không bao giờ hoàn thành).
Vòng đời của Observable bao gồm ba giai đoạn: tạo (create, just, from), biến đổi (toán tử map, filter, flatMap) và đăng ký (subscribe). Không có đăng ký, Observable không thực hiện bất kỳ hành động nào — nó là một chuỗi lười biếng. Sau khi đăng ký, Observable bắt đầu gửi sự kiện cho người đăng ký cho đến khi hoàn thành hoặc người đăng ký hủy đăng ký qua Disposable.
import RxSwift
// Tạo Observable từ mảng
let numbers = Observable.from([1, 2, 3, 4, 5])
// Biến đổi qua toán tử
let squared = numbers
.filter { $0 % 2 == 0 }
.map { $0 * $0 }
// Đăng ký với xử lý sự kiện
let disposable = squared
.subscribe(onNext: { print($0) },
onError: { print("Error: \($0)") },
onCompleted: { print("Done") })
Subject là một kiểu vừa là Observable (có thể đăng ký) vừa là Observer (có thể gửi sự kiện). RxSwift cung cấp bốn loại Subject: PublishSubject (chỉ sự kiện mới), BehaviorSubject (có giá trị ban đầu), ReplaySubject (lưu đệm N sự kiện gần nhất) và AsyncSubject (chỉ giá trị cuối cùng trước khi hoàn thành). Subject hữu ích để tích hợp mã mệnh lệnh (closure, delegate) vào chuỗi phản ứng.
Disposable là mã thông báo hủy đăng ký. Khi người đăng ký không muốn nhận sự kiện nữa, nó gọi disposable.dispose(). Trong thực tế, DisposeBag được sử dụng: một tập hợp các Disposable tự động hủy tất cả đăng ký khi chủ sở hữu bị hủy khởi tạo (ví dụ: UIViewController). Điều này ngăn rò rỉ bộ nhớ không thể tránh khỏi khi quản lý đăng ký thủ công.
| Loại Subject | Giá trị ban đầu | Gửi lại cho người đăng ký | Trường hợp sử dụng |
|---|---|---|---|
| PublishSubject | Không | Chỉ sự kiện mới | Sự kiện UI, thông báo hiếm |
| BehaviorSubject | Có | Cuối cùng + mới | Trạng thái, luồng dữ liệu |
| ReplaySubject | Không | Bộ đệm N + mới | Lưu cache lịch sử |
| AsyncSubject | Không | Chỉ cuối cùng | Tác vụ tính toán |
| PublishRelay | Không | Chỉ mới | Không có error/completed |
Toán tử trong RxSwift là các hàm nhận một Observable và trả về một Observable khác, biến đổi luồng dữ liệu. Bằng cách kết hợp các toán tử, nhà phát triển xây dựng chuỗi xử lý khai báo mà không có biến trung gian và điều kiện cạnh tranh. Tất cả toán tử đều lười biếng: chuỗi được xây dựng khi mô tả và thực thi khi đăng ký.
Toán tử biến đổi thay đổi mỗi sự kiện trong luồng. map áp dụng một hàm cho mỗi phần tử, flatMap mở Observable từ mỗi phần tử thành một luồng phẳng duy nhất, scan tích lũy kết quả trung gian (tương tự reduce nhưng phát ra mỗi bước). buffer nhóm các phần tử theo thời gian hoặc số lượng, window chia luồng thành các Observable lồng nhau theo cửa sổ.
// Ví dụ chuỗi toán tử: tìm kiếm với 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)
Toán tử lọc chỉ cho phép các sự kiện thỏa mãn điều kiện đi qua. filter lọc theo vị từ, distinctUntilChanged chỉ cho phép các giá trị khác với giá trị trước đó, take và takeWhile giới hạn số lượng sự kiện. skip và skipWhile bỏ qua N sự kiện đầu tiên hoặc sự kiện cho đến khi một điều kiện được đáp ứng. Để xử lý lỗi, catchError (chặn và thay thế) và retry (thử lại chuỗi khi có lỗi) được sử dụng.
Toán tử kết hợp kết hợp nhiều Observable thành một. combineLatest hợp nhất các giá trị mới nhất từ nhiều luồng thành một tuple, zip ghép cặp các phần tử có cùng chỉ mục, merge kết hợp nhiều luồng thành một theo thứ tự sự kiện đến. withLatestFrom kết hợp một sự kiện từ luồng chính với giá trị mới nhất từ luồng khác — hữu ích cho sự kiện UI với trạng thái hiện tại.
| Danh mục | Toán tử | Mô tả |
|---|---|---|
| Biến đổi | map | Biến đổi mỗi giá trị qua một hàm |
| Biến đổi | flatMap | Mở Observable lồng nhau thành luồng phẳng |
| Lọc | filter | Cho phép giá trị theo vị từ |
| Lọc | distinctUntilChanged | Chỉ cho phép giá trị đã thay đổi |
| Kết hợp | combineLatest | Kết hợp giá trị mới nhất từ 2+ luồng |
| Kết hợp | zip | Ghép cặp phần tử theo chỉ mục |
| Điều khiển | debounce | Trì hoãn sự kiện cho đến khi luồng tạm dừng |
| Điều khiển | take | Lấy N sự kiện đầu tiên và kết thúc luồng |
Scheduler trong RxSwift là một sự trừu tượng hóa trên luồng thực thi (hoặc hàng đợi). Scheduler xác định luồng nào thực hiện việc tạo sự kiện, biến đổi và đăng ký. Không giống như GCD, nơi nhà phát triển chỉ định rõ ràng DispatchQueue, RxSwift sử dụng hai tham số: subscribeOn (luồng nào thực thi Observable) và observeOn (luồng nào gọi người đăng ký).
MainScheduler thực thi mã trên luồng chính — bắt buộc để cập nhật UIKit (tất cả thay đổi UI phải xảy ra trên luồng chính). SerialDispatchQueueScheduler bao bọc một hàng đợi GCD nối tiếp, ConcurrentDispatchQueueScheduler bao bọc một hàng đợi đồng thời. Cho các tác vụ nền, ConcurrentDispatchQueueScheduler với qos: .background hoặc .utility được sử dụng.
// Mẫu điển hình: công việc nền + UI trên main
apiService.fetchData()
.subscribeOn(ConcurrentDispatchQueueScheduler(qos: .background))
.observeOn(MainScheduler.instance)
.subscribe(onNext: { data in
self.tableView.reloadData()
self.loadingIndicator.stopAnimating()
})
.disposed(by: disposeBag)
Sử dụng Scheduler đúng cách ngăn chặn chặn UI trong các yêu cầu mạng và tính toán nặng. subscribeOn ảnh hưởng đến toàn bộ thượng nguồn (tất cả toán tử trước observeOn thực thi trên bộ lập lịch được chỉ định). observeOn chuyển hạ nguồn — tất cả toán tử sau observeOn thực thi trên bộ lập lịch được chỉ định. Một chuỗi có thể có nhiều observeOn để chuyển đổi giữa các luồng ở các giai đoạn biến đổi khác nhau.
SerialDispatchQueueScheduler đảm bảo xử lý sự kiện tuần tự, điều quan trọng cho an toàn luồng của tài nguyên chia sẻ. Cho các hoạt động tải cao (xử lý hình ảnh, phân tích JSON), hãy sử dụng ConcurrentDispatchQueueScheduler — RxSwift sẽ giữ thứ tự sự kiện trong một Observable, nhưng các Observable khác nhau có thể được xử lý song song.
RxSwift trong các dự án iOS thường được sử dụng để liên kết UI với dữ liệu thông qua chuỗi phản ứng. RxCocoa cung cấp phần mở rộng rx cho tất cả thành phần UIKit tiêu chuẩn: rx.tap cho UIButton, rx.text cho UITextField, rx.selectedRow cho UIPickerView. Điều này cho phép từ bỏ @IBAction và delegate, thay thế chúng bằng đăng ký khai báo.
MVVM (Model-View-ViewModel) là một mẫu kiến trúc trong đó ViewModel quản lý trạng thái và logic nghiệp vụ, và View đăng ký các thuộc tính phản ứng của ViewModel. RxSwift phù hợp hoàn hảo với MVVM: ViewModel xuất bản Observable hoặc Driver cho dữ liệu, View đăng ký chúng qua bind. ViewModel không có tham chiếu đến View — nó trả về Observable mà View tiêu thụ.
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
}
}
}
// Trong ViewController:
viewModel.isFormValid
.bind(to: loginButton.rx.isEnabled)
.disposed(by: disposeBag)
Yêu cầu mạng là trường hợp sử dụng phổ biến thứ hai của RxSwift. URLSession.rx.response bao bọc một yêu cầu HTTP thành Observable trả về (response, data) khi thành công hoặc error khi thất bại. Kết hợp với Codable và toán tử map, ta có một lớp API phản ứng nhỏ gọn: Observable<MyModel> không cần callback và xử lý lỗi trong mỗi phương thức. Khi lỗi mạng, toán tử retry(3) tự động thử lại yêu cầu ba lần với backoff theo cấp số nhân.
Combine là framework gốc của Apple được giới thiệu trong iOS 13. Nó giải quyết các tác vụ giống như RxSwift: làm việc với sự kiện bất đồng bộ qua Publisher, Subscriber và toán tử. Tuy nhiên, Combine có những khác biệt chính: kiểu lỗi nghiêm ngặt qua kiểu Failure, hỗ trợ Swift Concurrency tích hợp (async/await) và tích hợp với SwiftUI qua @Published và ObservableObject.
RxSwift thắng về tương thích ngược — nó hỗ trợ iOS 8+, trong khi Combine yêu cầu iOS 13+. RxSwift có hệ sinh thái phong phú hơn các phần mở rộng bên thứ ba (RxDataSources, RxGesture, RxAnimated) và tài liệu toán tử chi tiết. Cho các dự án hỗ trợ phiên bản iOS cũ, RxSwift vẫn là lựa chọn duy nhất.
Cho các dự án mới với iOS 13+, Apple khuyến nghị Combine. Nó được tích hợp chặt chẽ với SwiftUI, có kích thước nhị phân nhỏ hơn và hỗ trợ chính thức. Tuy nhiên, di chuyển mã RxSwift hiện có sang Combine yêu cầu viết lại tất cả Observable → Publisher, và hệ sinh thái RxCocoa không có tương đương Combine đầy đủ — UIKit không có Publisher gốc cho tất cả thành phần UI.
| Đặc điểm | RxSwift | Combine |
|---|---|---|
| iOS tối thiểu | iOS 8+ | iOS 13+ |
| Loại lỗi | Error (bất kỳ) | Generic Failure |
| Mở rộng UI | RxCocoa (UIKit, AppKit) | @Published (SwiftUI) |
| Toán tử | 400+ toán tử | ~100 toán tử |
| Swift Concurrency | Qua cầu nối | Hỗ trợ gốc |
| Trạng thái | Bên thứ ba | Apple chính thức |
Câu hỏi thường gặp
Observable là nguồn sự kiện với thực thi lười biếng. Subject vừa là Observable vừa là Observer — bạn có thể đăng ký nó và gửi sự kiện mới thủ công. Subject hữu ích để tích hợp mã mệnh lệnh vào chuỗi phản ứng.
RxSwift được chọn khi cần hỗ trợ iOS 11-12, có cơ sở mã RxSwift hiện tại hoặc cần hệ sinh thái toán tử phong phú (400+ so với ~100 trong Combine). Cho các dự án mới trên iOS 13+, Combine được ưu tiên.
DisposeBag là một tập hợp các mã thông báo Disposable tự động hủy tất cả đăng ký khi chủ sở hữu bị hủy khởi tạo. Nếu không có DisposeBag, đăng ký tạo tham chiếu mạnh đến closure, dẫn đến rò rỉ bộ nhớ khi UIViewController bị hủy.
Sử dụng catchError để thay thế lỗi bằng giá trị mặc định, retry để thử lại thực thi Observable, materialize để biến lỗi thành sự kiện. Trong lớp UI, Driver và Signal không truyền lỗi — chúng được xử lý nội bộ.
Driver là một loại Observable đặc biệt đảm bảo thực thi trên MainScheduler, không có lỗi và chia sẻ tài nguyên. Driver đảm bảo cập nhật UI xảy ra trên luồng chính. Observable không cung cấp bảo đảm như vậy — cần observeOn.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm