響應式編程,響應式編程是一種面向數據流和變化傳播的編程方式式,簡單理解就是異步的數據流的開發。html
特色是將函數做爲一等公民,看成參數和返回值使用。典型的如OC和Swift 中的 map函數、filter函數、reduce函數等。每一個函數的處理結果給到下一個函數,最後的結果由自身函數調出。git
傳統實現方法:github
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) func buttonTapped() { print("button Tapped") } 複製代碼
經過 Rx 來實現:編程
button.rx.tap .subscribe(onNext: { print("button Tapped") }) .disposed(by: disposeBag) 複製代碼
你不須要使用 Target Action,這樣使得代碼邏輯清晰可見。swift
傳統實現方法:緩存
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() scrollView.delegate = self } } extension ViewController: UIScrollViewDelegate { func scrollViewDidScroll(_ scrollView: UIScrollView) { print("contentOffset: \(scrollView.contentOffset)") } } 複製代碼
經過 Rx 來實現:bash
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() scrollView.rx.contentOffset .subscribe(onNext: { contentOffset in print("contentOffset: \(contentOffset)") }) .disposed(by: disposeBag) } } 複製代碼
你不須要書寫代理的配置代碼,就能得到想要的結果。markdown
傳統實現方法:網絡
URLSession.shared.dataTask(with: URLRequest(url: url)) { (data, response, error) in guard error == nil else { print("Data Task Error: \(error!)") return } guard let data = data else { print("Data Task Error: unknown") return } print("Data Task Success with count: \(data.count)") }.resume() 複製代碼
經過 Rx 來實現:閉包
URLSession.shared.rx.data(request: URLRequest(url: url)) .subscribe(onNext: { data in print("Data Task Success with count: \(data.count)") }, onError: { error in print("Data Task Error: \(error)") }) .disposed(by: disposeBag) 複製代碼
回調也變得十分簡單
傳統實現方法:
override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.addObserver(self, selector: #selector(updateNotificationStatus), name: UIApplication.willEnterForegroundNotification, object: nil) } deinit { NotificationCenter.default.removeObserver(self) } // MARK: - Notification @objc func updateNotificationStatus(){ print("Application Will Enter Foreground") } 複製代碼
經過 Rx 來實現:
override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.rx.notification(UIApplication.willEnterForegroundNotification) .subscribe(onNext: { (notification) in print("Application Will Enter Foreground") }).disposed(by: disposeBag) } 複製代碼
你不須要去管理觀察者的生命週期,這樣你就有更多精力去關注業務邏輯。
傳統實現方法:
private var observerContext = 0 override func viewDidLoad() { super.viewDidLoad() user.addObserver(self, forKeyPath: #keyPath(User.name), options: [.new, .initial], context: &observerContext) } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if context == &observerContext { let newValue = change?[.newKey] as? String print("do something with newValue") } else { super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) } } deinit { user.removeObserver(self, forKeyPath: #keyPath(User.name)) } 複製代碼
經過 Rx 來實現:
override func viewDidLoad() { super.viewDidLoad() user.rx.observe(String.self, #keyPath(User.name)) .subscribe(onNext: { newValue in print("do something with newValue") }) .disposed(by: disposeBag) } 複製代碼
這樣實現 KVO 的代碼更清晰,更簡潔而且更準確。
例如,先經過用戶名密碼取得 Token 而後經過 Token 取得用戶信息,
傳統實現方法:
/// 用回調的方式封裝接口 enum API { /// 經過用戶名密碼取得一個 token static func token(username: String, password: String, success: (String) -> Void, failure: (Error) -> Void) { ... } /// 經過 token 取得用戶信息 static func userinfo(token: String, success: (UserInfo) -> Void, failure: (Error) -> Void) { ... } } 複製代碼
/// 經過用戶名和密碼獲取用戶信息 API.token(username: "beeth0ven", password: "987654321", success: { token in API.userInfo(token: token, success: { userInfo in print("獲取用戶信息成功: \(userInfo)") }, failure: { error in print("獲取用戶信息失敗: \(error)") }) }, failure: { error in print("獲取用戶信息失敗: \(error)") }) 複製代碼
經過 Rx 來實現:
/// 用 Rx 封裝接口 enum API { /// 經過用戶名密碼取得一個 token static func token(username: String, password: String) -> Observable<String> { ... } /// 經過 token 取得用戶信息 static func userInfo(token: String) -> Observable<UserInfo> { ... } } 複製代碼
/// 經過用戶名和密碼獲取用戶信息 API.token(username: "beeth0ven", password: "987654321") .flatMapLatest(API.userInfo) .subscribe(onNext: { userInfo in print("獲取用戶信息成功: \(userInfo)") }, onError: { error in print("獲取用戶信息失敗: \(error)") }) .disposed(by: disposeBag) 複製代碼
這樣你無需嵌套太多層,從而使得代碼易讀,易維護。
例如,須要將兩個網絡請求合併成一個,
經過 Rx 來實現:
/// 用 Rx 封裝接口 enum API { /// 取得老師的詳細信息 static func teacher(teacherId: Int) -> Observable<Teacher> { ... } /// 取得老師的評論 static func teacherComments(teacherId: Int) -> Observable<[Comment]> { ... } } 複製代碼
/// 同時取得老師信息和老師評論 Observable.zip( API.teacher(teacherId: teacherId), API.teacherComments(teacherId: teacherId) ).subscribe(onNext: { (teacher, comments) in print("獲取老師信息成功: \(teacher)") print("獲取老師評論成功: \(comments.count) 條") }, onError: { error in print("獲取老師信息或評論失敗: \(error)") }) .disposed(by: disposeBag) 複製代碼
這樣你可用寥寥幾行代碼來完成至關複雜的異步操做。
RxSwift 能夠在 UniDirectional Data Flow 的各個階段都發揮做用,從而讓 Data 的處理和流動更加簡潔和清晰。
MVVM 是 Model-View-ViewModel 的縮寫。
一樣使用 MVVM 架構時,Model 與 View|ViewControllter 之間是不容許直接通訊的,而是由 ViewModel 層進行協調
// 聲明一個枚舉,包含成功和失敗的狀況 enum TWSwiftHttpResult { case success(Any) //成功 case failure(String) //失敗 case noNet() //無網絡 } // 使用RxSwift進行擴展 extension Reactive where Base: TWSwiftHttpTool { /// 基於YYCache 緩存的RXSwift 請求方式 static func request(type: TWRequestType, url: String, parameters: [AnyHashable: Any]?, flag: Bool = true, isCache: Bool = false, cacheKey: String? = nil, cacheBlock: (CacheBlock)? = nil) -> Observable<Any> { return Observable.create { observer in let task = TWSwiftHttpTool.request(type: type, url: url, flag: flag, parameters: parameters, isCache: isCache, cacheKey: cacheKey, cacheBlock: cacheBlock, complete: { (result) in dealComplete(result: result, observer: observer) }) return Disposables.create(with: task.cancel) } } // MARK: - Private Common Method private static func dealComplete(result:TWSwiftHttpResult,observer:AnyObserver<Any>) { switch result { case .success(let response): observer.onNext(response) observer.onCompleted() case .failure(let reason): // observer.onNext([TWSwiftErrorMsg:reason]) observer.onError(RxNetworkError.general(reason)) case .noNet: // observer.onNext([TWSwiftErrorMsg:TWSwiftNoNetMsg]) observer.onError(RxNetworkError.noNet) } } } 複製代碼
//訂閱輸入輸出協議 protocol TWSwiftViewModelProtocol { associatedtype TWSwiftInput associatedtype TWSwiftOutput func transform(input: TWSwiftInput) -> TWSwiftOutput } //viewModel實現TWSwiftViewModelProtocol的協議 extension NewHouseWeekViewModel:TWSwiftViewModelProtocol{ typealias TWSwiftInput = Input typealias TWSwiftOutput = Output // MARK: - Override Method struct Input { //便是訂閱又是被訂閱 let requestId = PublishSubject<String>() } struct Output { //輸出數據源 let sections: Driver<[NewHouseWeekSectionModel]> //成功輸出 let successSubject = PublishSubject<String>() //錯誤輸出 let errorSubject = PublishSubject<String>() init(sections: Driver<[NewHouseWeekSectionModel]>) { self.sections = sections } } func transform(input: NewHouseWeekViewModel.Input) -> NewHouseWeekViewModel.Output { ``` } } 複製代碼
/// viewModel綁定 fileprivate func bindViewModel() { let vmInput = NewHouseWeekViewModel.Input() ///輸入 let vmOutput = viewModel.transform(input: vmInput) ///輸出 ///數據源綁定到tableview中 vmOutput.sections.asDriver().drive(tableView.rx.items(dataSource: dataSource)).disposed(by: TWSwiftDisposeBag) ///錯誤訂閱 vmOutput.errorSubject.subscribe(onNext:{[weak self] (errorMsg) in self?.hideHud(in: self?.tableView, hint: errorMsg) self?.setEmptyDic() }).disposed(by: TWSwiftDisposeBag) ///成功訂閱 vmOutput.successSubject.subscribe(onNext: nil, onError: nil, onCompleted: {[weak self] in self?.hideHUD() self?.setEmptyDic() }, onDisposed: nil).disposed(by: TWSwiftDisposeBag) let regionId = TWSwiftGuardNullString(GlobalObject.share()?.regionId) ///發起數據請求 vmInput.requestId.onNext(regionId) self.showActivityIndicatorSuperView(tableView) } 複製代碼
理解響應鏈的編程思路 RxSwift給咱們帶來最大影響的Reactive思想,OOP告訴咱們,在編寫應用程序的時候,要考慮的是對象有什麼,對象作什麼,對象與對象之間的聯繫,而Reactive思想將對象所作的都當作是數據流,咱們關注的是事件自己的影響。