RxSwift คือไลบรารีการเขียนโปรแกรมเชิงปฏิกิริยาสำหรับ iOS ที่ใช้รูปแบบ Observable และตัวดำเนินการเชิงฟังก์ชันสำหรับทำงานกับสตรีมข้อมูลแบบอะซิงโครนัส โดยนำแนวคิดของ ReactiveX (Rx) มาสู่ภาษา Swift โดยมี API แบบรวมสำหรับจัดการเหตุการณ์จาก UI คำขอเครือข่าย ฐานข้อมูลและตัวจับเวลา ตามข้อมูลของ ReactiveX, 2025 ไลบรารีนี้ถูกใช้ในโครงการมากกว่า 60,000 โครงการบน GitHub และยังคงเป็นมาตรฐานการเขียนโปรแกรมเชิงปฏิกิริยาสำหรับ Swift จนกระทั่ง Combine ดั้งเดิมถือกำเนิดขึ้น RxSwift แทนที่ delegates closures และการแจ้งเตือนด้วยห่วงโซ่การประมวลผลเหตุการณ์เดียว
ประเด็นสำคัญ
RxSwift คือพอร์ต Swift ของไลบรารี ReactiveX (Rx) ที่สร้างโดย Microsoft สำหรับ .NET ในปี 2012 แนวคิดหลักของ Rx คือการแสดงแหล่งข้อมูลแบบอะซิงโครนัสใดๆ เป็นลำดับ Observable ซึ่งสามารถใช้ตัวดำเนินการเชิงฟังก์ชันได้ ในการพัฒนา iOS RxSwift ใช้สำหรับเชื่อมโยงเหตุการณ์ UI (การกดปุ่ม การป้อนข้อความ ท่าทาง) เข้ากับตรรกะของแอปพลิเคชันโดยไม่มี delegates ที่ชัดเจนและ target-action
ไลบรารีประกอบด้วยสามโมดูล: RxSwift (แกนกลาง — Observable, Operator, Scheduler), RxCocoa (การรวม UIKit — ส่วนขยาย rx สำหรับ UIButton, UITextField, UITableView) และ RxRelay (Subject ที่ไม่มีเหตุการณ์สิ้นสุด) การแยกนี้ทำให้สามารถใช้แกนกลางใน Swift ฝั่งเซิร์ฟเวอร์และการทดสอบ โดยเชื่อมต่อตัวหุ้ม UI สำหรับแอปพลิเคชัน iOS เท่านั้น
จากการสำรวจของ Stack Overflow, 2025 พบว่า RxSwift อยู่ใน 5 อันดับแรกของไลบรารีบุคคลที่สามที่ใช้มากที่สุดสำหรับ iOS เหตุผลหลักของความนิยมคือการจัดการรูปแบบอะซิงโครนัสทั้งหมดอย่างสม่ำเสมอ: คำขอเครือข่ายผ่าน URLSession แอนิเมชันผ่าน UIViewPropertyAnimator การแจ้งเตือนผ่าน NotificationCenter และ delegates ผ่าน DelegateProxy — ทั้งหมดนี้สรุปเป็น Observable ที่มีวงจรชีวิตที่คาดเดาได้
การเขียนโปรแกรมเชิงปฏิกิริยา เป็นกระบวนทัศน์ที่โปรแกรมตอบสนองต่อการเปลี่ยนแปลงข้อมูลแทนที่จะสอบถามข้อมูล แทนที่จะเขียน “รับข้อมูล → ประมวลผล → อัปเดต UI” นักพัฒนาจะอธิบายห่วงโซ่การเปลี่ยนแปลง: “เมื่อข้อมูลเปลี่ยน ให้ใช้ตัวกรอง จากนั้น map จากนั้นอัปเดต UI” RxSwift ใช้กระบวนทัศน์นี้ผ่านห่วงโซ่ Observable ที่มีการดำเนินการแบบขี้เกียจ — จะไม่มีอะไรเกิดขึ้นจนกว่าจะมีผู้สมัครสมาชิกปรากฏขึ้น
Observable เป็นชนิดพื้นฐานของ RxSwift ที่แสดงลำดับของเหตุการณ์ในช่วงเวลา Observable สามารถส่งเหตุการณ์สามประเภท: next (ค่าใหม่), error (ข้อผิดพลาดที่สิ้นสุดสตรีม) และ completed (การเสร็จสมบูรณ์สำเร็จ) มี Observable แบบจำกัด (เสร็จสมบูรณ์หลังจากส่งค่าทั้งหมด) และ Observable แบบไม่จำกัด (เช่น เหตุการณ์ UI — ไม่เคยเสร็จสมบูรณ์)
วงจรชีวิต ของ Observable ประกอบด้วยสามขั้นตอน: การสร้าง (create, just, from), การเปลี่ยนรูป (ตัวดำเนินการ map, filter, flatMap) และการสมัครสมาชิก (subscribe) หากไม่มีการสมัครสมาชิก Observable จะไม่ดำเนินการใดๆ — มันเป็นลำดับแบบขี้เกียจ หลังจากการสมัครสมาชิก Observable จะเริ่มส่งเหตุการณ์ไปยังผู้สมัครสมาชิกจนกว่าจะเสร็จสมบูรณ์หรือผู้สมัครสมาชิกยกเลิกการสมัครผ่าน Disposable
import RxSwift
// การสร้าง Observable จากอาร์เรย์
let numbers = Observable.from([1, 2, 3, 4, 5])
// การเปลี่ยนรูปผ่านตัวดำเนินการ
let squared = numbers
.filter { $0 % 2 == 0 }
.map { $0 * $0 }
// การสมัครสมาชิกพร้อมจัดการเหตุการณ์
let disposable = squared
.subscribe(onNext: { print($0) },
onError: { print("Error: \($0)") },
onCompleted: { print("Done") })
Subject เป็นชนิดที่เป็นทั้ง Observable (สามารถสมัครสมาชิกได้) และ Observer (สามารถส่งเหตุการณ์ไปยังมันได้) RxSwift มี Subject สี่ประเภท: PublishSubject (เฉพาะเหตุการณ์ใหม่), BehaviorSubject (มีค่าเริ่มต้น), ReplaySubject (บัฟเฟอร์ N เหตุการณ์ล่าสุด) และ AsyncSubject (เฉพาะค่าสุดท้ายก่อนเสร็จสมบูรณ์) Subject มีประโยชน์สำหรับการรวมโค้ดแบบ Imperative (closures, delegates) เข้ากับห่วงโซ่เชิงปฏิกิริยา
Disposable คือโทเค็นการยกเลิกการสมัครสมาชิก เมื่อผู้สมัครสมาชิกไม่ต้องการรับเหตุการณ์อีกต่อไป มันจะเรียก disposable.dispose() ในทางปฏิบัติ จะใช้ DisposeBag: ชุดของ Disposable ที่ยกเลิกการสมัครสมาชิกทั้งหมดโดยอัตโนมัติเมื่อยกเลิกการเริ่มต้นเจ้าของ (เช่น UIViewController) ซึ่งป้องกันการรั่วไหลของหน่วยความจำที่หลีกเลี่ยงไม่ได้เมื่อจัดการการสมัครสมาชิกด้วยตนเอง
| ประเภท Subject | ค่าเริ่มต้น | ส่งซ้ำให้ผู้สมัครสมาชิก | กรณีใช้งาน |
|---|---|---|---|
| PublishSubject | ไม่มี | เฉพาะเหตุการณ์ใหม่ | เหตุการณ์ UI การแจ้งเตือนที่หายาก |
| BehaviorSubject | มี | ล่าสุด + ใหม่ | สถานะ สตรีมข้อมูล |
| ReplaySubject | ไม่มี | บัฟเฟอร์ N + ใหม่ | การแคชประวัติ |
| AsyncSubject | ไม่มี | เฉพาะล่าสุด | งานคำนวณ |
| PublishRelay | ไม่มี | เฉพาะใหม่ | ไม่มี error/completed |
ตัวดำเนินการ ใน RxSwift คือฟังก์ชันที่รับ Observable หนึ่งและส่งคืน Observable อีกตัว โดยเปลี่ยนรูปสตรีมข้อมูล การรวมตัวดำเนินการทำให้นักพัฒนาสร้างห่วงโซ่การประมวลผลแบบ Declarative โดยไม่มีตัวแปรกลางและสภาวะการแข่งขัน ตัวดำเนินการทั้งหมดขี้เกียจ: ห่วงโซ่ถูกสร้างขึ้นเมื่ออธิบายและดำเนินการเมื่อสมัครสมาชิก
ตัวดำเนินการเปลี่ยนรูป เปลี่ยนแต่ละเหตุการณ์ในสตรีม map ใช้ฟังก์ชันกับแต่ละองค์ประกอบ flatMap คลาย Observable จากแต่ละองค์ประกอบเป็นสตรีมเรียบเดียว scan สะสมผลลัพธ์ระหว่างกลาง (คล้าย reduce แต่ปล่อยแต่ละขั้นตอน) buffer จัดกลุ่มองค์ประกอบตามเวลาหรือจำนวน window แบ่งสตรีมเป็น Observable ที่ซ้อนกันตามหน้าต่าง
// ตัวอย่างห่วงโซ่ตัวดำเนินการ: ค้นหาด้วย 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)
ตัวดำเนินการกรอง อนุญาตเฉพาะเหตุการณ์ที่ตรงตามเงื่อนไข filter กรองตามภาคแสดง distinctUntilChanged อนุญาตเฉพาะค่าที่แตกต่างจากค่าก่อนหน้า take และ takeWhile จำกัดจำนวนเหตุการณ์ skip และ skipWhile ข้าม N เหตุการณ์แรกหรือเหตุการณ์จนกว่าเงื่อนไขจะเป็นจริง สำหรับการจัดการข้อผิดพลาด จะใช้ catchError (สกัดกั้นและแทนที่) และ retry (ลองลำดับใหม่เมื่อเกิดข้อผิดพลาด)
ตัวดำเนินการรวม รวม Observable หลายตัวเป็นหนึ่งเดียว combineLatest รวมค่าล่าสุดจากหลายสตรีมเป็น tuple zip จับคู่องค์ประกอบที่มีดัชนีเดียวกัน merge รวมหลายสตรีมเป็นหนึ่งตามลำดับการมาถึงของเหตุการณ์ withLatestFrom รวมเหตุการณ์จากสตรีมหลักกับค่าล่าสุดจากสตรีมอื่น — มีประโยชน์สำหรับเหตุการณ์ UI ที่มีสถานะปัจจุบัน
| หมวดหมู่ | ตัวดำเนินการ | คำอธิบาย |
|---|---|---|
| เปลี่ยนรูป | map | เปลี่ยนรูปแต่ละค่าผ่านฟังก์ชัน |
| เปลี่ยนรูป | flatMap | คลาย Observable ที่ซ้อนกันเป็นสตรีมเรียบ |
| กรอง | filter | อนุญาตค่าตามภาคแสดง |
| กรอง | distinctUntilChanged | อนุญาตเฉพาะค่าที่เปลี่ยนแปลง |
| รวม | combineLatest | รวมค่าล่าสุดจาก 2+ สตรีม |
| รวม | zip | จับคู่องค์ประกอบตามดัชนี |
| ควบคุม | debounce | หน่วงเหตุการณ์จนกว่าสตรีมหยุดชั่วคราว |
| ควบคุม | take | รับ N เหตุการณ์แรกและสิ้นสุดสตรีม |
Scheduler ใน RxSwift คือการทำให้เป็นนามธรรมเหนือเธรดการดำเนินการ (หรือคิว) Scheduler กำหนดว่าเหตุการณ์ การเปลี่ยนรูปและการสมัครสมาชิกจะดำเนินการบนเธรดใด ต่างจาก GCD ที่นักพัฒนาระบุ DispatchQueue อย่างชัดเจน RxSwift ใช้สองพารามิเตอร์: subscribeOn (เธรดใดที่ Observable ดำเนินการ) และ observeOn (เธรดใดที่เรียกผู้สมัครสมาชิก)
MainScheduler ดำเนินการโค้ดบนเธรดหลัก — จำเป็นสำหรับการอัปเดต UIKit (การเปลี่ยนแปลง UI ทั้งหมดต้องเกิดขึ้นบนเธรดหลัก) SerialDispatchQueueScheduler ห่อหุ้มคิว GCD แบบอนุกรม ConcurrentDispatchQueueScheduler ห่อหุ้มคิวแบบพร้อมกัน สำหรับงานเบื้องหลัง ใช้ ConcurrentDispatchQueueScheduler ด้วย qos: .background หรือ .utility
// รูปแบบทั่วไป: งานเบื้องหลัง + UI บน main
apiService.fetchData()
.subscribeOn(ConcurrentDispatchQueueScheduler(qos: .background))
.observeOn(MainScheduler.instance)
.subscribe(onNext: { data in
self.tableView.reloadData()
self.loadingIndicator.stopAnimating()
})
.disposed(by: disposeBag)
การใช้ Scheduler อย่างถูกต้องป้องกันการบล็อก UI ระหว่างคำขอเครือข่ายและการคำนวณหนัก subscribeOn ส่งผลต่อต้นน้ำทั้งหมด (ตัวดำเนินการทั้งหมดก่อน observeOn ดำเนินการบนตัวกำหนดตารางเวลาที่ระบุ) observeOn สลับปลายน้ำ — ตัวดำเนินการทั้งหมดหลัง observeOn ดำเนินการบนตัวกำหนดตารางเวลาที่ระบุ ห่วงโซ่อาจมีหลาย observeOn สำหรับสลับระหว่างเธรดในขั้นตอนการเปลี่ยนรูปต่างๆ
SerialDispatchQueueScheduler รับประกันการประมวลผลเหตุการณ์ตามลำดับ ซึ่งสำคัญสำหรับความปลอดภัยของเธรดของทรัพยากรที่ใช้ร่วมกัน สำหรับการดำเนินการที่มีภาระสูง (การประมวลผลภาพ การแยกวิเคราะห์ JSON) ให้ใช้ ConcurrentDispatchQueueScheduler — RxSwift จะรักษาลำดับเหตุการณ์ภายใน Observable เดียว แต่ Observable ต่างๆ อาจถูกประมวลผลแบบขนาน
RxSwift ในโครงการ iOS มักใช้สำหรับเชื่อมโยง UI กับข้อมูลผ่านห่วงโซ่เชิงปฏิกิริยา RxCocoa ให้ส่วนขยาย rx สำหรับส่วนประกอบ UIKit มาตรฐานทั้งหมด: rx.tap สำหรับ UIButton, rx.text สำหรับ UITextField, rx.selectedRow สำหรับ UIPickerView ซึ่งช่วยให้ละทิ้ง @IBAction และ delegates แทนที่ด้วยการสมัครสมาชิกแบบ Declarative
MVVM (Model-View-ViewModel) เป็นรูปแบบสถาปัตยกรรมที่ ViewModel จัดการสถานะและตรรกะทางธุรกิจ และ View สมัครสมาชิกคุณสมบัติเชิงปฏิกิริยาของ ViewModel RxSwift เข้ากันได้อย่างสมบูรณ์กับ MVVM: ViewModel เผยแพร่ Observable หรือ Driver สำหรับข้อมูล View สมัครสมาชิกผ่าน bind ViewModel ไม่มีการอ้างอิงถึง View — มันส่งคืน Observable ที่ View ใช้
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
}
}
}
// ใน ViewController:
viewModel.isFormValid
.bind(to: loginButton.rx.isEnabled)
.disposed(by: disposeBag)
คำขอเครือข่ายเป็นกรณีการใช้งานที่พบบ่อยเป็นอันดับสองของ RxSwift URLSession.rx.response ห่อหุ้มคำขอ HTTP ใน Observable ที่ส่งคืน (response, data) เมื่อสำเร็จหรือ error เมื่อล้มเหลว เมื่อรวมกับ Codable และตัวดำเนินการ map จะได้ชั้น API เชิงปฏิกิริยาที่กะทัดรัด: Observable<MyModel> โดยไม่มี callbacks และการจัดการข้อผิดพลาดในแต่ละวิธี เมื่อเกิดข้อผิดพลาดเครือข่าย ตัวดำเนินการ retry(3) จะลองคำขอใหม่โดยอัตโนมัติสามครั้งด้วยการหน่วงเวลาแบบเอกซ์โปเนนเชียล
Combine เป็น framework ดั้งเดิมของ Apple ที่เปิดตัวใน iOS 13 โดยแก้ปัญหาเดียวกันกับ RxSwift: การทำงานกับเหตุการณ์แบบอะซิงโครนัสผ่าน Publisher, Subscriber และตัวดำเนินการ อย่างไรก็ตาม Combine มีความแตกต่างสำคัญ: การกำหนดชนิดข้อผิดพลาดที่เข้มงวดผ่านชนิด Failure การรองรับ Swift Concurrency (async/await) ในตัว และการรวมกับ SwiftUI ผ่าน @Published และ ObservableObject
RxSwift ชนะในด้านความเข้ากันได้ย้อนหลัง — รองรับ iOS 8+ ในขณะที่ Combine ต้องการ iOS 13+ RxSwift มีระบบนิเวศส่วนขยายของบุคคลที่สามที่สมบูรณ์กว่า (RxDataSources, RxGesture, RxAnimated) และเอกสารตัวดำเนินการโดยละเอียด สำหรับโครงการที่รองรับ iOS เวอร์ชันเก่า RxSwift ยังคงเป็นตัวเลือกเดียว
สำหรับโครงการใหม่บน iOS 13+ Apple แนะนำ Combine มันถูกรวมเข้ากับ SwiftUI อย่างแน่นหนา มีขนาดไบนารีที่เล็กกว่าและการสนับสนุนอย่างเป็นทางการ อย่างไรก็ตาม การย้ายโค้ด RxSwift ที่มีอยู่ไปยัง Combine ต้องเขียน Observable → Publisher ใหม่ทั้งหมด และระบบนิเวศ RxCocoa ไม่มีสิ่งที่เทียบเท่า Combine อย่างสมบูรณ์ — UIKit ไม่มี Publisher ดั้งเดิมสำหรับส่วนประกอบ UI ทั้งหมด
| คุณลักษณะ | RxSwift | Combine |
|---|---|---|
| iOS ขั้นต่ำ | iOS 8+ | iOS 13+ |
| ชนิดข้อผิดพลาด | Error (ใดๆ) | Generic Failure |
| ส่วนขยาย UI | RxCocoa (UIKit, AppKit) | @Published (SwiftUI) |
| ตัวดำเนินการ | 400+ ตัวดำเนินการ | ~100 ตัวดำเนินการ |
| Swift Concurrency | ผ่านบริดจ์ | รองรับดั้งเดิม |
| สถานะ | บุคคลที่สาม | Apple อย่างเป็นทางการ |
คำถามที่พบบ่อย
Observable คือแหล่งที่มาของเหตุการณ์ที่มีการดำเนินการแบบขี้เกียจ Subject เป็นทั้ง Observable และ Observer — คุณสามารถสมัครสมาชิกและส่งเหตุการณ์ใหม่ด้วยตนเอง Subject มีประโยชน์สำหรับการรวมโค้ดแบบ Imperative เข้ากับห่วงโซ่เชิงปฏิกิริยา
RxSwift ถูกเลือกเมื่อต้องการรองรับ iOS 11-12 เมื่อมีโค้ดเบส RxSwift ที่มีอยู่ หรือเมื่อต้องการระบบนิเวศตัวดำเนินการที่สมบูรณ์ (400+ เทียบกับ ~100 ใน Combine) สำหรับโครงการใหม่บน iOS 13+ ควรเลือก Combine
DisposeBag คือชุดของโทเค็น Disposable ที่ยกเลิกการสมัครสมาชิกทั้งหมดโดยอัตโนมัติเมื่อยกเลิกการเริ่มต้นเจ้าของ หากไม่มี DisposeBag การสมัครสมาชิกจะสร้างการอ้างอิงแบบ strong ไปยัง closure ซึ่งนำไปสู่การรั่วไหลของหน่วยความจำเมื่อ UIViewController ถูกทำลาย
ใช้ catchError เพื่อแทนที่ข้อผิดพลาดด้วยค่าเริ่มต้น retry เพื่อลองดำเนินการ Observable อีกครั้ง materialize เพื่อแปลงข้อผิดพลาดเป็นเหตุการณ์ ในชั้น UI Driver และ Signal จะไม่ส่งข้อผิดพลาด — พวกมันถูกจัดการภายใน
Driver เป็น Observable ชนิดพิเศษที่รับประกันการดำเนินการบน MainScheduler การไม่มีข้อผิดพลาดและการแบ่งปันทรัพยากร Driver รับประกันว่าการอัปเดต UI เกิดขึ้นบนเธรดหลัก Observable ไม่ได้ให้การรับประกันดังกล่าว — จำเป็นต้องมี observeOn
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม