SwiftHub 是大神Khoren Markosyan 寫的一個徹底採用Rxswift + MVVM + Moya 的架構的項目,代碼很精簡,想學習MVVM架構的認真去研究這個項目的設計,對你之後的編程思想和習慣都會有很大的幫助。(點擊這裏下載:SwiftHub源碼 )react
下載源碼後,進入SwiftHub-master主目錄,先要下載安裝第三方庫,若是你cd SwiftHub-master/
就直接執行pod install
的話通常都會報錯:ios
分析報錯緣由不難看出,已經提示咱們須要先pod repo update
一下更新你本地的cocos pod庫。git
可能有的小夥伴網速不太好,pod install一直更新不了,這裏提供了一份我編譯好的源碼:連接:pan.baidu.com/s/1qwkjY_Zr… 密碼:60t7github
我只能驚歎,哇塞,怎麼用了這麼多第三方框架啊,我我的觀點是不太主張用太多第三方框架,能本身實現都本身實現,除非要實現的功能必需要用第三方框架。由於第三方框架會大大增長咱們ipa包的大小,對於ipa大小有要求的是個災難,例如以前咱們有一個項目使用Realm
做爲DB框架,可是發現這個框架實在是太佔內存了足足有將近90MB,而我只是想裏面一個小小的數據庫存儲相關的代碼,後面改爲WCDB.swift
框架,這個框架只有2MB左右。算法
下面咱們先來看一下SwiftHub 項目用到的第三方框架吧: shell
# Uncomment the next line to define a global platform for your project
platform :ios, '11.0'
use_frameworks!
inhibit_all_warnings!
target 'SwiftHub' do
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
# Pods for SwiftHub
# Networking
pod 'Moya/RxSwift', '14.0.0-beta.2' # https://github.com/Moya/Moya
pod 'Apollo', '0.19.0' # https://github.com/apollographql/apollo-ios
# Rx Extensions
pod 'RxDataSources', '~> 4.0' # https://github.com/RxSwiftCommunity/RxDataSources
pod 'RxSwiftExt', '~> 5.0' # https://github.com/RxSwiftCommunity/RxSwiftExt
pod 'NSObject+Rx', '~> 5.0' # https://github.com/RxSwiftCommunity/NSObject-Rx
pod 'RxViewController', '~> 1.0' # https://github.com/devxoul/RxViewController
pod 'RxGesture', '~> 3.0' # https://github.com/RxSwiftCommunity/RxGesture
pod 'RxOptional', '~> 4.0' # https://github.com/RxSwiftCommunity/RxOptional
pod 'RxTheme', '~> 4.0' # https://github.com/RxSwiftCommunity/RxTheme
#pod 'RxAnimated', '~> 0.4' # https://github.com/RxSwiftCommunity/RxAnimated
# JSON Mapping
#pod 'ObjectMapper', :git => 'https://github.com/kajensen/ObjectMapper.git' # https://github.com/Hearst-DD/ObjectMapper
pod 'Moya-ObjectMapper/RxSwift', :git => 'https://github.com/khoren93/Moya-ObjectMapper.git', :branch => 'moya14' # https://github.com/ivanbruel/Moya-ObjectMapper
# Image
pod 'Kingfisher', '~> 5.0' # https://github.com/onevcat/Kingfisher
# Date
pod 'DateToolsSwift', '~> 4.0' # https://github.com/MatthewYork/DateTools
pod 'SwiftDate', '~> 6.0' # https://github.com/malcommac/SwiftDate
# Tools
pod 'R.swift', '~> 5.0' # https://github.com/mac-cain13/R.swift
pod 'SwiftLint', '0.37.0' # https://github.com/realm/SwiftLint
# Keychain
pod 'KeychainAccess', '~> 4.0' # https://github.com/kishikawakatsumi/KeychainAccess
# Fabric
pod 'Fabric'
pod 'Crashlytics'
# UI
pod 'NVActivityIndicatorView', '~> 4.0' # https://github.com/ninjaprox/NVActivityIndicatorView
pod 'ImageSlideshow/Kingfisher', '~> 1.8' # https://github.com/zvonicek/ImageSlideshow
pod 'DZNEmptyDataSet', '~> 1.0' # https://github.com/dzenbot/DZNEmptyDataSet
pod 'Hero', '~> 1.5.0' # https://github.com/lkzhao/Hero
pod 'Localize-Swift', '~> 3.0' # https://github.com/marmelroy/Localize-Swift
pod 'RAMAnimatedTabBarController', '~> 5.0' # https://github.com/Ramotion/animated-tab-bar
pod 'AcknowList', '~> 1.8' # https://github.com/vtourraine/AcknowList
pod 'KafkaRefresh', '~> 1.0' # https://github.com/OpenFeyn/KafkaRefresh
pod 'WhatsNewKit', '~> 1.0' # https://github.com/SvenTiigi/WhatsNewKit
pod 'Highlightr', '~> 2.0' # https://github.com/raspu/Highlightr
pod 'DropDown', '~> 2.0' # https://github.com/AssistoLab/DropDown
pod 'Toast-Swift', '~> 5.0' # https://github.com/scalessec/Toast-Swift
pod 'HMSegmentedControl', '~> 1.0' # https://github.com/HeshamMegid/HMSegmentedControl
pod 'FloatingPanel', '~> 1.0' # https://github.com/SCENEE/FloatingPanel
pod 'MessageKit', '~> 3.0' # https://github.com/MessageKit/MessageKit
pod 'MultiProgressView', '~> 1.0' # https://github.com/mac-gallagher/MultiProgressView
# Keyboard
pod 'IQKeyboardManagerSwift', '~> 6.0' # https://github.com/hackiftekhar/IQKeyboardManager
# Auto Layout
pod 'SnapKit', '~> 5.0' # https://github.com/SnapKit/SnapKit
# Code Quality
pod 'FLEX', :git => 'https://github.com/khoren93/FLEX.git', :branch => 'remove_private_api' # https://github.com/Flipboard/FLEX
pod 'SwifterSwift', '~> 5.0' # https://github.com/SwifterSwift/SwifterSwift
pod 'BonMot', '~> 5.0' # https://github.com/Rightpoint/BonMot
# Logging
pod 'CocoaLumberjack/Swift', '~> 3.0' # https://github.com/CocoaLumberjack/CocoaLumberjack
# Analytics
# https://github.com/devxoul/Umbrella
pod 'Umbrella/Mixpanel', '~> 0.8'
pod 'Umbrella/Firebase'
pod 'Mixpanel', '~> 3.0' # https://github.com/mixpanel/mixpanel-iphone
pod 'Firebase/Analytics'
# Ads
pod 'Firebase/AdMob'
pod 'Google-Mobile-Ads-SDK', '7.52.0'
target 'SwiftHubTests' do
inherit! :search_paths
# Pods for testing
pod 'Quick', '~> 2.0' # https://github.com/Quick/Quick
pod 'Nimble', '~> 8.0' # https://github.com/Quick/Nimble
#pod 'RxNimble', '~> 4.0' # https://github.com/RxSwiftCommunity/RxNimble
pod 'RxAtomic', :modular_headers => true
pod 'RxBlocking' # https://github.com/ReactiveX/RxSwift
pod 'Firebase'
end
end
target 'SwiftHubUITests' do
inherit! :search_paths
# Pods for testing
end
post_install do |installer|
# Cocoapods optimization, always clean project after pod updating
Dir.glob(installer.sandbox.target_support_files_root + "Pods-*/*.sh").each do |script|
flag_name = File.basename(script, ".sh") + "-Installation-Flag"
folder = "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
file = File.join(folder, flag_name)
content = File.read(script)
content.gsub!(/set -e/, "set -e\nKG_FILE=\"#{file}\"\nif [ -f \"$KG_FILE\" ]; then exit 0; fi\nmkdir -p \"#{folder}\"\ntouch \"$KG_FILE\"")
File.write(script, content)
end
# Enable tracing resources
installer.pods_project.targets.each do |target|
if target.name == 'RxSwift'
target.build_configurations.each do |config|
if config.name == 'Debug'
config.build_settings['OTHER_SWIFT_FLAGS'] ||= ['-D', 'TRACE_RESOURCES']
end
end
end
end
end
複製代碼
Alamofire和AFNetwork 是一對兄弟,是出自同一個公司的產品, 它是一個很好的Swift編寫的網絡框架庫,提供了HTTP相關接口,能輕鬆實現鏈式請求和響應,能實現文件上傳,下載,斷點續傳,後臺下載等功能。數據庫
安裝方式:編程
pod 'Alamofire', '~> 5.1'
github "Alamofire/Alamofire" ~> 5.1
安裝環境要求:json
iOS 10.0+ / macOS 10.12+ / tvOS 10.0+ / watchOS 3.0+ Xcode 11+ Swift 5.1+swift
提供的功能特性:
- 可鏈請求/響應方法
- URL / JSON參數編碼
- 上傳文件/數據/流/ MultipartFormData
- 使用請求或簡歷數據下載文件
- 身份驗證與URLCredential
- HTTP響應驗證
- 上傳和下載進度閉包與進度
- cURL命令輸出
- 動態調整和重試請求
- TLS證書和公鑰固定
- 網絡可達性
- 全面的單元和集成測試覆蓋
- 完整的文檔
爲了讓 Alamofire更專一於處理網絡相關的事情,Alamofire軟件基金會已經建立了額外的組件庫來爲Alamofire生態系統帶來額外的功能。如:AlamofireImage庫和 AlamofireNetworkActivityIndicator
- AlamofireImage: 一個圖像庫,包括圖像響應序列化器,UIImage和UIImageView擴展,自定義圖像過濾器,一個自動清除內存緩存和一個基於優先級的圖像下載系統。
- AlamofireNetworkActivityIndicator : 使用Alamofire控制iOS上的網絡活動指示器的可見性。它包含可配置的延遲計時器,以幫助減小閃爍,並支持不受Alamofire管理的URLSession實例。
Alamofire框架結構圖:
關於Alamofire的使用能夠參考個人一些博客:
- 設置請求url
- 設置URLRequest對象,配置請求相關信息
- 建立會話配置URLSessionConfiguration
- 建立會話URLSession
- 建立任務和設置請求回調,併發起請求
簡單請求代碼:
func responseData() {
let url = "http://onapp.kongyulu.top/public/?s=api/test/list"
Alamofire.request(url).responseJSON {
(response) in
switch response.result{
case .success(let json):
print("json:\(json)")
let dict = json as! Dictionary<String, Any>
let list = dict["data"] as! Array<AnyObject>
guard let result = [UserModel1].deserialize(from: list) else{return}
self.observable.onNext(result as [Any])
break
case .failure(let error):
print("error:\(error)")
break
}
}
}
複製代碼
其中URLSessionConfiguration提供了框架的相關配置:
主要提供瞭如下3中方式:
default
:默認模式,經常使用模式,在該模式下系統會建立持久化緩存,並在用戶的鑰匙串中保存證書ephemeral
:不支持持久性存儲,全部內容的會隨着session的生命週期結束而釋放background
:與default模式相似,在該模式下會建立一個獨立線程來傳輸網絡請求數據,能夠在後臺乃至APP關閉的時候也能夠進行數據傳輸
let configuration = URLSessionConfiguration.background(withIdentifier: "request_id")
let session = URLSession.init(configuration: configuration, delegate: self, delegateQueue: OperationQueue.main)
session.dataTask(with: request) { (data, response, error) in
do {
let list = try JSONSerialization.jsonObject(with: data!, options: .allowFragments)
print(list)
}catch{
print(error)
}
}.resume()
複製代碼
此外還提供了不少屬性來按需配置
identifier
:配置對象的後臺會話標識符httpAdditionalHeaders
:與請求一塊兒發送的附加頭文件字典networkServiceType
:網絡服務的類型allowsCellularAccess
:一個布爾值,用因而否應經過蜂窩網絡進行鏈接timeoutIntervalForRequest
:等待附加數據的超時時間timeoutIntervalForResource
:資源請求容許的最大時間範圍sharedContainerIdentifier
:應將後臺URL會話中的文件下載到的共享容器的標識符waitsForConnectivity
:一個布爾值,指示會話是否應等待鏈接變爲可用仍是當即失敗
httpCookieAcceptPolicy
:決定什麼時候接受cookie的策略常量httpShouldSetCookies
:一個布爾值,肯定請求是否包含來自cookie存儲區的cookiehttpCookieStorage
:用於會話中存儲cookie的cookie存儲區HTTPCookie
:該對象爲不可變對象,從包含cookie屬性的字典初始化,支持兩個不一樣的cookie版本,v0、v1
TLS協議
:用於在兩個通訊應用程序之間提供保密性和數據完整性tlsMaximumSupportedProtocol
:在此會話中創建鏈接時客戶端應請求的最大TLS協議版本tlsMinimumSupportedProtocol
:協議協商期間應接受的最小TLS協議urlCredentialStorage
:爲身份驗證提供憑據的憑據存儲區
urlCache
:用於爲會話中的請求提供緩存響應的URL緩存requestCachePolicy
:決定什麼時候從緩存中返回響應的預約義常量
sessionSendsLaunchEvents
:一個布爾值,指示當傳輸完成時,應用程序應在後臺恢復仍是啓動isDiscretionary
:一個布爾值,用於肯定後臺任務是否能夠由系統自行安排已得到最佳性能shouldUseExtendedBackgroundIdleMode
:一個布爾值,指示當應用程序轉移到後臺時是否應保持TCP鏈接打開
protocolClasses
:在會話中處理請求的額外協議子類的數組URLProtocol
:該對象用來處理加載協議特定URL數據
multipathServiceType
:指定用於經過Wi-Fi和蜂窩接口傳輸數據的多路徑TCP鏈接策略的服務類型
httpMaximumConnectionsPerHost
:同時鏈接到給定主機的最大數量httpShouldUsePipelining
:一個布爾值,用於肯定會話是否使用HTTP流水線connectionProxyDictionary
:包含相關要在此會話中使用的代理信息的字典
waitsForConnectivity
:一個布爾值,指示會話應等待鏈接可用仍是當即失敗
Rxswift家族提供很是好的函數響應式編程框架,使用Rxswift編寫代碼可讓代碼變得很是簡潔,邏輯清晰,若是配合Moya + Rxswift + MVVM架構,真的是很完美,這個開源項目SwiftHub就是這樣的一個完美的項目。
ReactiveX(簡寫:Rx)是一個能夠幫助咱們簡化異步編程的框架。而 RxSwift 是 Rx 的 Swift 版本。除了 RxSwift,還有 RxJava、RxJS、Rx.Net 等,對應的OC 版本則是 RAC(ReactiveCocoa),這裏是 RxSwift 的 github 地址 ,已經有了將近 18.2K 顆星了。
RxSwift
: RxSwift的核心,提供由ReactiveX(主要)定義的Rx標準。它沒有其餘依賴項。 RxCocoa
: 爲通常的iOS/macOS/watchOS & tvOS應用程序開發提供特定於cocoa的功能,如綁定、特性等。它同時依賴於RxSwift和RxRelay。 RxRelay
: 提供發佈中繼和行爲中繼,這兩個簡單的主題包裝器。這取決於RxSwift。 RxTest and RxBlocking
: 爲基於rx的系統提供測試功能。這取決於RxSwift。
RxSwift (三)Observable的建立,訂閱,銷燬
RxSwift(五)(Rxswift對比swift,oc用法)
RxSwift學習之十二 (基礎使用篇 3- UI控件擴展)
Rxswift一些簡單使用以下:
//MARK: - RxSwift應用-button響應
func setupButton() {
// 傳統UI事件
self.button.addTarget(self, action: #selector(didClickButton), for: .touchUpInside)
// 這樣的操做 - 不行啊!代碼邏輯與事件邏輯分層
self.button.rx.tap
.subscribe(onNext: { [weak self] in
print("點了,小雞燉蘑菇")
self?.view.backgroundColor = UIColor.orange
})
.disposed(by: disposeBag)
}
複製代碼
//MARK: - RxSwift應用-textfiled
func setupTextFiled() {
// 咱們若是要對輸入的文本進行操做 - 好比輸入的的內容 而後咱們獲取裏面的偶數
// self.textFiled.delegate = self
// 感受是否是特別噁心
// 下面咱們來看看Rx
self.textFiled.rx.text.orEmpty.changed.subscribe(onNext: { (text) in
print("監聽到了 - \(text)")
}).disposed(by: disposeBag)
self.textFiled.rx.text.bind(to: self.button.rx.title()).disposed(by: disposeBag)
}
複製代碼
//MARK: - RxSwift應用-scrollView
func setupScrollerView() {
scrollView.rx.contentOffset.subscribe(onNext: { [weak self] (content) in
self?.view.backgroundColor = UIColor.init(red: content.y/255.0*0.8, green: content.y/255.0*0.3, blue: content.y/255.0*0.6, alpha: 1);
print(content.y)
}).disposed(by: disposeBag)
}
複製代碼
//MARK: - RxSwift應用-KVO
func setupKVO() {
// 系統KVO 仍是比較麻煩的
// person.addObserver(self, forKeyPath: "name", options: .new, context: nil)
person.rx.observeWeakly(String.self, "name").subscribe(onNext: { (change) in
print(change ?? "helloword")
}).disposed(by: disposeBag)
}
複製代碼
//MARK: - 通知
func setupNotification(){
NotificationCenter.default.rx
.notification(UIResponder.keyboardWillShowNotification)
.subscribe { (event) in
print(event)
}.disposed(by: disposeBag)
}
複製代碼
//MARK: - 手勢
func setupGestureRecognizer(){
let tap = UITapGestureRecognizer()
self.label.addGestureRecognizer(tap)
self.label.isUserInteractionEnabled = true
tap.rx.event.subscribe { (event) in
print("點了label")
}.disposed(by: disposeBag)
}
複製代碼
//MARK: - RxSwift應用-網絡請求
func setupNextwork() {
let url = URL(string: "https://www.baidu.com")
URLSession.shared.rx.response(request: URLRequest(url: url!))
.subscribe(onNext: { (response, data) in
print("response ==== \(response)")
print("data ===== \(data)")
}, onError: { (error) in
print("error ===== \(error)")
}).disposed(by: disposeBag)
}
複製代碼
//MARK: - RxSwift應用-timer定時器
func setupTimer() {
timer = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
timer.subscribe(onNext: { (num) in
print("hello word \(num)")
}).disposed(by: disposeBag)
}
複製代碼
Moya是一個網絡抽象層,它在底層將Alamofire進行封裝,對外提供更簡潔的接口供開發者調用。在Objective-C中,大部分開發者會使用AFNetwork進行網絡請求,當業務複雜一些時,會對AFNetwork進行二次封裝,編寫一個適用於本身項目的網絡抽象層。在Objective-C中,有著名的YTKNetwork,它將AFNetworking封裝成抽象父類,而後根據每一種不一樣的網絡請求,都編寫不一樣的子類,子類繼承父類,來實現請求業務。Moya在項目層次中的地位,有點相似於YTKNetwork。能夠看下圖對比
若是單純把Moya等同於swift版的YTKNetwork,那就是比較錯誤的想法了。Moya的設計思路和YTKNetwork差距很是大。上面我在介紹YTKNetwork時在強調子類和父類,繼承,是由於YTKNetwork是比較經典的利用OOP思想(面向對象)設計的產物。基於swift的Moya雖然也有使用到繼承,可是它的總體上是以POP思想(Protocol Oriented Programming,面向協議編程)爲主導的。
Provider
:provider
是一個提供網絡請求服務的提供者。經過一些初始化配置以後,在外部能夠直接用provider來發起request。Request
:在使用Moya
進行網絡請求時,第一步須要進行配置,來生成一個Request。首先按照官方文檔,建立一個枚舉,遵照TargetType
協議,並實現協議所規定的屬性。爲何要建立枚舉來遵照協議,枚舉結合switch
語句,使得API管理起來比較方便。- 根據建立了一個遵照
TargetType
協議的名爲Myservice的枚舉,咱們完成了以下幾個變量的設置。
baseURL
path
method
sampleData
task
headers
複製代碼
import UIKit
import Moya
import RxCocoa
import Result
import SwiftyJSON
//初始rovider
let KApiProvider = MoyaProvider<KNetworkAPI>(plugins: [RequestLoadingPlugin()])
let K_Search_Base = "http://www.baid.com/search"
/** 請求的endpoints)**/
//請求分類
enum KNetworkAPI {
case shareNavList:
case shareList(pageSize: Int, pageNum: Int):
}
//請求配置
extension KNetworkAPI: TargetType {
//服務器地址
public var baseURL: URL {
switch self {
default:
return URL(string: K_Search_Base)!
}
}
//各個請求的具體路徑
public var path: String {
switch self {
case .shareNavList:
return "manage/navigation/getNavigationList"
default:
return "default/list"
}
}
//請求類型
public var method: Moya.Method {
switch self {
default:
return .get
}
}
//請求任務事件(這裏附帶上參數)
public var task: Task {
switch self {
case .shareNavList:
return .requestPlain
case .shareList(let pageSize, let pageNum):
var params: [String: Any] = [:]
params["pageSize"] = pageSize
params["pageNum"] = pageNum
return .requestParameters(parameters: params, encoding: URLEncoding.default)
}
}
//是否執行Alamofire驗證
public var validate: Bool {
return false
}
//這個就是作單元測試模擬的數據,
// 只會在單元測試文件中有做用
public var sampleData: Data {
return "{}".data(using: String.Encoding.utf8)!
}
//請求頭
public var headers: [String: String]? {
switch self {
default:
return ["Content-type": "application/json"]
}
}
}
RequestLoadingPlugin 插件用來顯示UI相關,捕獲網絡異常等操做,給出提示,代碼以下:
```swift
import UIKit
import Foundation
import MBProgressHUD
import Moya
import Result
class RequestLoadingPlugin: PluginType {
func prepare(_ request: URLRequest, target: TargetType) -> URLRequest {
print("prepare")
var mRequest = request
mRequest.timeoutInterval = 20
return mRequest
}
func willSend(_ request: RequestType, target: TargetType) {
print("開始請求")
if SwiftIsShowHud == true {
let keyViewController = UIApplication.shared.keyWindow?.rootViewController
if (keyViewController != nil) {
MBProgressHUD.showAdded(to: keyViewController!.view, animated: true)
}
}
}
func didReceive(_ result: Result<Response, MoyaError>, target: TargetType) {
print("結束請求")
let keyViewController = UIApplication.shared.keyWindow?.rootViewController
if (keyViewController != nil) {
MBProgressHUD.hide(for: keyViewController!.view, animated: true)
// MBProgressHUD.
}
guard case Result.failure(_) = result
else {
let respons = result.value
let dic: Dictionary<String, Any>? =
try? JSONSerialization.jsonObject(with: respons!.data, options: .mutableContainers) as! Dictionary<String, Any>
if dic != nil {
if dic?.keys.contains("status") == true {
if dic?["status"] as! Int == 11 || dic?["status"] as! Int == 12 {
print("Token 失效")
}
}
if dic?.keys.contains("code") == true {
if dic?["code"] as! Int == 11 || dic?["code"] as! Int == 12 {
print("Token 失效")
}
}
}
return
}
let errorReason: String = (result.error?.errorDescription)!
print("請求失敗:\(errorReason)")
var tip = ""
if errorReason.contains("The Internet connection appears to be offline") {
tip = "網絡不給力,請檢查您的網絡"
}else if errorReason.contains("Could not connect to the server") {
tip = "沒法鏈接服務器"
}else {
tip = "請求失敗"
}
/// 使用tip文字 進行提示
}
}
複製代碼
import RxSwift
import RxCocoa
import ObjectMapper
KApiProvider.rx.request(input.category)
.mapObject(KBaseModel<T>.self)
.subscribe(onSuccess: { (baseModel) in
print("請求成功 返回數據以下")
if baseModel.status != 0 {
return
}
}, onError: {error in
print("Error:請求錯誤")
}).disposed(by: self.disposeBag)
}, onError: { (error) in
}, onCompleted: {
}) {
}.disposed(by: disposeBag)
複製代碼
ObjectMapper 是一個使用 Swift 語言編寫的數據模型轉換框架,咱們能夠方便的將模型對象轉換爲JSON,或者JSON生成相應的模型類。
有以下特色:
- 將JSON映射到對象
- 將對象映射到JSON
- 支持嵌套對象(在數組或字典中單獨使用)
- 支持映射過程當中自定義轉換
- 支持結構體
- 支持Immutable
建立模型類須要實現
Mappable
接口,包括init?(map: Map)
和func mapping(map: Map)
兩個方法 ObjectMapper使用<-特殊運算符表示JSON
與模型屬性之間的映射關係
實例代碼以下:
class User: Mappable {
var username: String?
var age: Int?
var weight: Double!
var array: [Any]?
var dictionary: [String : Any] = [:]
var bestFriend: User? // Nested User object
var friends: [User]? // Array of Users
var birthday: Date?
//對象序列號以前驗證JSON合法性,不符合條件返回nil阻止映射發生
required init?(map: Map) {
// 檢查JSON是否有name字段
if map.JSON["name"] == nil {
return nil
}
}
// Mappable
func mapping(map: Map) {
username <- map["username"]
age <- map["age"]
weight <- map["weight"]
array <- map["arr"]
dictionary <- map["dict"]
bestFriend <- map["best_friend"]
friends <- map["friends"]
birthday <- (map["birthday"], DateTransform())
}
}
struct Temperature: Mappable {
var celsius: Double?
var fahrenheit: Double?
init?(map: Map) {
}
mutating func mapping(map: Map) {
celsius <- map["celsius"]
fahrenheit <- map["fahrenheit"]
}
}
複製代碼
let user = User(JSONString: JSONString)
//使用Mapper
let user = Mapper<User>().map(JSONString: JSONString)
//使用Mapper轉模型數組
let users: [User] = Mapper<User>().mapArray(JSONString: JSONString)
複製代碼
//prettyPrint參數是爲了打印可讀性json
let JSONString = user.toJSONString(prettyPrint: true)
//使用Mapper
let JSONString = Mapper().toJSONString(users, prettyPrint: true)
複製代碼
Int
Bool
Double
Float
String
RawRepresentable (Enums)
Array<Any>
Dictionary<String, Any>
Object<T: Mappable>
Array<T: Mappable>
Array<Array<T: Mappable>>
Set<T: Mappable>
Dictionary<String, T: Mappable>
Dictionary<String, Array<T: Mappable>>
Optionals of all the above //上述的可選類型
Implicitly Unwrapped Optionals of the above //上述的隱式解析可選類型
複製代碼
import ObjectMapper
class UserInfo: Mappable {
var username: String?
var age: Int?
var weight: Double!
var dictionary: UserInfo?
var value: String?
required init?(map: Map) {
}
func mapping(map: Map) {
username <- map["username"]
age <- map["age"]
weight <- map["weight"]
dictionary <- map["dictionary"]
value <- map["dictionary.username"]
}
}
複製代碼
DateTransform
、DataTransform
、HexColorTransform
,可是沒有提供的就須要咱們自定義,下面舉例實現NSURLTransform
import UIKit
import ObjectMapper
class NSURLTransform: TransformType {
typealias Object = NSURL
typealias JSON = String
func transformFromJSON(_ value: Any?) -> NSURL? {
guard let string = value as? String else{
return nil
}
return NSURL.init(string: string)
}
func transformToJSON(_ value: NSURL?) -> String? {
guard let url = value else{
return nil
}
return url.absoluteString
}
}
複製代碼
此外,還有一個比較好用的框架AlamofireObjectMapper:
該框架能夠結合 Alamofire 和 ObjectMapper 使用, 爲Alamofire的Request類擴展出了
responseObject
和responseArray
方法, 更方便的將網絡通訊返回的JSON
數據轉換成對象下面是它的樣列代碼: ![]()
let URL = "..."
Alamofire.request(.GET, URL).responseObject { (response: DataResponse<WeatherResponse>) in
let weatherResponse = response.result.value
if let threeDayForecast = weatherResponse?.threeDayForecast {
for forecast in threeDayForecast {
print(forecast.day)
print(forecast.temperature)
}
}
}
複製代碼
安裝方式:
pod 'Moya-ObjectMapper'
#The subspec if you want to use the bindings over RxSwift.
pod 'Moya-ObjectMapper/RxSwift'
#The subspec if you want to use the bindings over ReactiveSwift.
pod 'Moya-ObjectMapper/ReactiveSwift'
複製代碼
import Foundation
import ObjectMapper
// MARK: Initializer and Properties
struct Repository: Mappable {
var identifier: Int!
var language: String?
var url: String!
// MARK: JSON
init?(map: Map) { }
mutating func mapping(map: Map) {
identifier <- map["id"]
language <- map["language"]
url <- map["url"]
}
}
複製代碼
沒有Rxswift 和 ReactiveSwift 的使用方法:
GitHubProvider.request(.userRepositories(username), completion: { result in
var success = true
var message = "Unable to fetch from GitHub"
switch result {
case let .success(response):
do {
if let repos = try response.mapArray(Repository) {
self.repos = repos
} else {
success = false
}
} catch {
success = false
}
self.tableView.reloadData()
case let .failure(error):
guard let error = error as? CustomStringConvertible else {
break
}
message = error.description
success = false
}
})
複製代碼
Rxswift的使用方式:
GitHubProvider.request(.userRepositories(username))
.mapArray(Repository.self)
.subscribe { event -> Void in
switch event {
case .next(let repos):
self.repos = repos
case .error(let error):
print(error)
default: break
}
}.addDisposableTo(disposeBag)
複製代碼
ReactiveSwift的使用方式:
GitHubProvider.request(.userRepositories(username))
.mapArray(Repository.self)
.start { event in
switch event {
case .value(let repos):
self.repos = repos
case .failed(let error):
print(error)
default: break
}
}
複製代碼
ReactiveSwift提供了可組合的、聲明性的和靈活的原語,這些原語是圍繞着隨時間流逝的價值流的宏大概念構建的。 這些原語能夠用來統一地表示常見的Cocoa
和泛型編程模式,它們本質上是一種觀察行爲,例如委託模式、回調閉包、通知、控制操做、響應鏈事件和鍵值觀察(KVO
)。 由於全部這些不一樣的機制均可以用相同的方式表示,因此很容易以聲明的方式將它們組合在一塊兒,用更少的意大利麪條代碼和狀態來彌補差距。
- O(N)計算差別的算法: 該算法假設全部的部分和項都是惟一的,所以沒有歧義。 若是有歧義,回退自動對非動畫刷新。
- 它應用額外的啓發式方法,向分段視圖發送最少數量的命令: 儘管運行時間是線性的,但發送命令的首選數量一般比線性少得多 最好(也可能)將更改的數量限制在較小的範圍內,若是更改的數量增加爲線性,則只需進行正常的從新加載
- 支持擴展項目和節結構: 用IdentifiableType和Equatable擴展你的項目,用AnimatableSectionModelType擴展你的部分
- 支持兩個層次動畫的全部組合的節和項目: 節動畫:插入,刪除,移動 項目動畫:插入、刪除、移動、重載(若是舊值不等於新值)
- 可配置的動畫類型插入,重載和刪除(自動,淡出,…)
- 示例應用程序
- 隨機壓力測試(示例app)
- 支持開箱即用的編輯(示例應用程序)
- 適用於UITableView和UICollectionView
安裝:
CocoaPods
Podfile
pod 'RxDataSources', '~> 4.0'
複製代碼
Carthage
Cartfile
github "RxSwiftCommunity/RxDataSources" ~> 4.0
複製代碼
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, Int>>(configureCell: configureCell)
Observable.just([SectionModel(model: "title", items: [1, 2, 3])])
.bind(to: tableView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
複製代碼
若是您正在使用Rxswift,您可能會遇到內置操做符不能提供所需功能的狀況。爲了不膨脹,Rxswift內核被設計得儘量緊湊。這個存儲庫的目的是提供額外的方便操做符和反應性擴展。
安裝: RxSwiftExt的這個分支以Swift 5爲目標。x和Rxswift 5.0.0或更高版本。
若是您正在尋找RxSwiftExt的Swift 4版本,請使用該框架的3.4.0版本。
CocoaPods
Add to your Podfile:
pod 'RxSwiftExt', '~> 5'
複製代碼
這將同時安裝RxSwift和RxCocoa擴展。若是您只想安裝RxSwift擴展,而不想安裝RxCocoa擴展,只需使用:
pod 'RxSwiftExt/Core'
複製代碼
Using Swift 4:
pod 'RxSwiftExt', '~> 3'
複製代碼
Carthage
github "RxSwiftCommunity/RxSwiftExt"
複製代碼
RxSwiftExt擴展了以下操做:
unwrap
: 打開選項並過濾掉空值。Observable.of(1,2,nil,Int?(4))
.unwrap()
.subscribe { print($0) }
複製代碼
結果:
next(1)
next(2)
next(4)
複製代碼
ignore
:忽略特定元素。Observable.from(["One","Two","Three"])
.ignore("Two")
.subscribe { print($0) }
複製代碼
結果:
next(One)
next(Three)
completed
複製代碼
ignoreWhen
:根據閉包忽略元素。Observable<Int>
.of(1,2,3,4,5,6)
.ignoreWhen { $0 > 2 && $0 < 6 }
.subscribe { print($0) }
複製代碼
結果:
next(1)
next(2)
next(6)
completed
複製代碼
once
:將下一個元素精確地發送一次到接收它的第一個訂閱服務器。進一步的訂閱者將獲得一個空序列。let obs = Observable.once("Hello world")
print("First")
obs.subscribe { print($0) }
print("Second")
obs.subscribe { print($0) }
複製代碼
結果:
First
next(Hello world)
completed
Second
completed
複製代碼
distinct
:只有在序列中從未出現過元素時,纔將它們傳遞過去。Observable.of("a","b","a","c","b","a","d")
.distinct()
.subscribe { print($0) }
複製代碼
結果:
next(a)
next(b)
next(c)
next(d)
completed
複製代碼
mapTo
:用提供的值替換每一個元素。Observable.of(1,2,3)
.mapTo("Nope.")
.subscribe { print($0) }
複製代碼
結果:
next(Nope.)
next(Nope.)
next(Nope.)
completed
複製代碼
mapAt
:將每一個元素轉換爲提供的鍵路徑上的值。struct Person {
let name: String
}
Observable
.of(
Person(name: "Bart"),
Person(name: "Lisa"),
Person(name: "Maggie")
)
.mapAt(\.name)
.subscribe { print($0) }
複製代碼
結果:
next(Bart)
next(Lisa)
next(Maggie)
completed
複製代碼
not
:否認的布爾值。Observable.just(false)
.not()
.subscribe { print($0) }
複製代碼
結果:
next(true)
completed
複製代碼
and
:驗證發出的每一個值都爲真Observable.of(true, true)
.and()
.subscribe { print($0) }
Observable.of(true, false)
.and()
.subscribe { print($0) }
Observable<Bool>.empty()
.and()
.subscribe { print($0) }
複製代碼
結果:
success(true)
success(false)
completed
複製代碼
cascade
:順序級聯經過一系列可觀察對象,當一個可觀察對象在列表的更下方開始發射元素時,當即放棄以前的訂閱。let a = PublishSubject<String>()
let b = PublishSubject<String>()
let c = PublishSubject<String>()
Observable.cascade([a,b,c])
.subscribe { print($0) }
a.onNext("a:1")
a.onNext("a:2")
b.onNext("b:1")
a.onNext("a:3")
c.onNext("c:1")
a.onNext("a:4")
b.onNext("b:4")
c.onNext("c:2")
複製代碼
結果:
next(a:1)
next(a:2)
next(b:1)
next(c:1)
next(c:2)
複製代碼
pairwise
:將一個可觀察對象發出的元素分組成數組,其中每一個數組由最後兩個連續的項組成;相似於滑動窗口。Observable.from([1, 2, 3, 4, 5, 6])
.pairwise()
.subscribe { print($0) }
複製代碼
結果:
next((1, 2))
next((2, 3))
next((3, 4))
next((4, 5))
next((5, 6))
completed
複製代碼
nwise
:將一個可觀察對象發出的元素分組成數組,其中每一個數組由最後的N個連續項組成;相似於滑動窗口。Observable.from([1, 2, 3, 4, 5, 6])
.nwise(3)
.subscribe { print($0) }
複製代碼
結果:
next([1, 2, 3])
next([2, 3, 4])
next([3, 4, 5])
next([4, 5, 6])
completed
複製代碼
retry
:在發生錯誤或成功終止以前,使用給定的行爲重複源觀察到的序列。有四種具備不一樣謂詞和延遲選項的行爲:immediate、delayed、exponentialDelayed和customTimerDelayed。// in case of an error initial delay will be 1 second,
// every next delay will be doubled
// delay formula is: initial * pow(1 + multiplier, Double(currentAttempt - 1)), so multiplier 1.0 means, delay will doubled
_ = sampleObservable.retry(.exponentialDelayed(maxCount: 3, initial: 1.0, multiplier: 1.0), scheduler: delayScheduler)
.subscribe(onNext: { event in
print("Receive event: \(event)")
}, onError: { error in
print("Receive error: \(error)")
})
複製代碼
結果:
Receive event: First
Receive event: Second
Receive event: First
Receive event: Second
Receive event: First
Receive event: Second
Receive error: fatalError
複製代碼
repeatWithBehavior
:當源觀察序列完成時,使用給定的行爲重複它。此操做符接受與重試操做符相同的參數。有四種具備不一樣謂詞和延遲選項的行爲:immediate、delayed、exponentialDelayed和customTimerDelayed。// when the sequence completes initial delay will be 1 second,
// every next delay will be doubled
// delay formula is: initial * pow(1 + multiplier, Double(currentAttempt - 1)), so multiplier 1.0 means, delay will doubled
_ = completingObservable.repeatWithBehavior(.exponentialDelayed(maxCount: 3, initial: 1.0, multiplier: 1.2), scheduler: delayScheduler)
.subscribe(onNext: { event in
print("Receive event: \(event)")
})
複製代碼
結果:
Receive event: First
Receive event: Second
Receive event: First
Receive event: Second
Receive event: First
Receive event: Second
複製代碼
catchErrorJustComplete
:當發生錯誤時,取消錯誤條件,完成一個序列let _ = sampleObservable
.do(onError: { print("Source observable emitted error \($0), ignoring it") })
.catchErrorJustComplete()
.subscribe {
print ("\($0)")
}
複製代碼
結果:
next(First)
next(Second)
Source observable emitted error fatalError, ignoring it
completed
複製代碼
pausable
:暫停源觀察序列的元素,除非來自第二個觀察序列的最新元素爲真。let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
let trueAtThreeSeconds = Observable<Int>.timer(3, scheduler: MainScheduler.instance).map { _ in true }
let falseAtFiveSeconds = Observable<Int>.timer(5, scheduler: MainScheduler.instance).map { _ in false }
let pauser = Observable.of(trueAtThreeSeconds, falseAtFiveSeconds).merge()
let pausedObservable = observable.pausable(pauser)
let _ = pausedObservable
.subscribe { print($0) }
複製代碼
結果:
next(2)
next(3)
複製代碼
apply
:Apply爲在可觀察的序列上應用轉換提供了一種統一的機制,而沒必要擴展ObservableType或重複您的轉換。更多的理由見github上的討論// An ordinary function that applies some operators to its argument, and returns the resulting Observable
func requestPolicy(_ request: Observable<Void>) -> Observable<Response> {
return request.retry(maxAttempts)
.do(onNext: sideEffect)
.map { Response.success }
.catchError { error in Observable.just(parseRequestError(error: error)) }
// We can apply the function in the apply operator, which preserves the chaining style of invoking Rx operators
let resilientRequest = request.apply(requestPolicy)
複製代碼
filterMap
:Rx中的一個常見模式是過濾掉一些值,而後將其他的值映射到其餘值。filterMap容許你一步完成:// keep only even numbers and double them
Observable.of(1,2,3,4,5,6)
.filterMap { number in
(number % 2 == 0) ? .ignore : .map(number * 2)
}
複製代碼
上面的序列保持偶數二、四、6,併產生序列四、八、12。
errors, elements
:這些操做符只適用於使用materialize()操做符(來自RxSwift core)物化的可觀察序列。錯誤返回一個通過過濾的錯誤事件序列,即拋出的元素。元素返回一個通過過濾的元素事件序列,拋出錯誤。let imageResult = _chooseImageButtonPressed.asObservable()
.flatMap { imageReceiver.image.materialize() }
.share()
let image = imageResult
.elements()
.asDriver(onErrorDriveWith: .never())
let errorMessage = imageResult
.errors()
.map(mapErrorMessages)
.unwrap()
.asDriver(onErrorDriveWith: .never())
複製代碼
fromAsync
:將簡單的異步完成處理程序轉換爲可觀察的序列。適合與僅使用一個參數調用完成處理程序的現有異步服務一塊兒使用。發出由完成處理程序生成的結果,而後完成。func someAsynchronousService(arg1: String, arg2: Int, completionHandler:(String) -> Void) {
// a service that asynchronously calls
// the given completionHandler
}
let observableService = Observable
.fromAsync(someAsynchronousService)
observableService("Foo", 0)
.subscribe(onNext: { (result) in
print(result)
})
.disposed(by: disposeBag)
複製代碼
zip(with:)
:便利版的Observable.zip(_:)。將指定的可觀察序列合併爲一個可觀察序列,只要全部的可觀察序列在相應的索引處產生一個元素,就使用選擇器函數。let first = Observable.from(numbers)
let second = Observable.from(strings)
first.zip(with: second) { i, s in
s + String(i)
}.subscribe(onNext: { (result) in
print(result)
})
複製代碼
結果:
next("a1")
next("b2")
next("c3")
複製代碼
merge(with:)
:便利版的Observable.merge(_:)。將可觀察序列中的元素與不一樣的可觀察序列中的元素合併爲一個可觀察序列。let oddStream = Observable.of(1, 3, 5)
let evenStream = Observable.of(2, 4, 6)
let otherStream = Observable.of(1, 5, 6)
oddStream.merge(with: evenStream, otherStream)
.subscribe(onNext: { result in
print(result)
})
複製代碼
結果:
1 2 1 3 4 5 5 6 6
複製代碼
ofType
:ofType操做符過濾可觀察序列的元素(若是它是提供的類型的實例)。Observable.of(NSNumber(value: 1),
NSDecimalNumber(string: "2"),
NSNumber(value: 3),
NSNumber(value: 4),
NSDecimalNumber(string: "5"),
NSNumber(value: 6))
.ofType(NSDecimalNumber.self)
.subscribe { print($0) }
複製代碼
結果:
next(2)
next(5)
completed
複製代碼
withUnretained
:withunretain (_:resultSelector:)操做符提供了一個未保留的、能夠安全使用(即不隱式取消包裝)的對象引用,以及序列發出的事件。若是提供的對象不能成功保留,則seqeunce將完成class TestClass: CustomStringConvertible {
var description: String { return "Test Class" }
}
Observable
.of(1, 2, 3, 5, 8, 13, 18, 21, 23)
.withUnretained(testClass)
.do(onNext: { _, value in
if value == 13 {
// When testClass becomes nil, the next emission of the original
// sequence will try to retain it and fail. As soon as it fails,
// the sequence will complete.
testClass = nil
}
})
.subscribe()
複製代碼
結果:
next((Test Class, 1))
next((Test Class, 2))
next((Test Class, 3))
next((Test Class, 5))
next((Test Class, 8))
next((Test Class, 13))
completed
複製代碼
count
:在一個可觀察對象終止且沒有錯誤時發出的項數。若是給定一個謂詞,則只計算與謂詞匹配的元素。Observable.from([1, 2, 3, 4, 5, 6])
.count { $0 % 2 == 0 }
.subscribe()
複製代碼
結果:
next(3)
completed
複製代碼
partition
:將一個流劃分爲兩個單獨的元素流,這兩個元素流與提供的謂詞匹配或不匹配。let numbers = Observable
.of(1, 2, 3, 4, 5, 6)
let (evens, odds) = numbers.partition { $0 % 2 == 0 }
_ = evens.debug("even").subscribe() // emits 2, 4, 6
_ = odds.debug("odds").subscribe() // emits 1, 3, 5
複製代碼
bufferWithTrigger
:收集源可觀察到的元素,並在觸發器發出時將它們做爲數組發出。let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
let signalAtThreeSeconds = Observable<Int>.timer(3, scheduler: MainScheduler.instance).map { _ in () }
let signalAtFiveSeconds = Observable<Int>.timer(5, scheduler: MainScheduler.instance).map { _ in () }
let trigger = Observable.of(signalAtThreeSeconds, signalAtFiveSeconds).merge()
let buffered = observable.bufferWithTrigger(trigger)
buffered.subscribe { print($0) }
// prints next([0, 1, 2]) @ 3, next([3, 4]) @ 5
複製代碼
若是你用Rxswift通常你常常須要這樣子let disposeBag = DisposeBag()
定義一個垃圾袋對象,用來銷燬回收序列的資源。每一個類中都要去定義這樣一個東東是很麻煩的。而NSObject+Rx幫你簡化了這部操做,你能夠不須要定義let disposeBag = DisposeBag()
這樣的代碼了,直接ob.rx.disposeBag
就能夠了,例如:
thing
.bind(to: otherThing)
.disposed(by: rx.disposeBag)
複製代碼
Add to your Podfile:
pod 'NSObject+Rx'
複製代碼
Carthage
Add to Cartfile:
github "RxSwiftCommunity/NSObject-Rx"
複製代碼
RxViewController是用於UIViewController和NSViewController的RxSwift包裝器。
有了RxViewController的包裝後,你能夠這樣在VC中調用viewDidLoad
方法:
self.rx.viewDidLoad
.subscribe(onNext: {
print("viewDidLoad 🎉")
})
複製代碼
此外RxViewController還提供瞭如下這些API:
extension Reactive where Base: UIViewController {
var viewDidLoad: ControlEvent<Void>
var viewWillAppear: ControlEvent<Bool>
var viewDidAppear: ControlEvent<Bool>
var viewWillDisappear: ControlEvent<Bool>
var viewDidDisappear: ControlEvent<Bool>
var viewWillLayoutSubviews: ControlEvent<Void>
var viewDidLayoutSubviews: ControlEvent<Void>
var willMoveToParentViewController: ControlEvent<UIViewController?>
var didMoveToParentViewController: ControlEvent<UIViewController?>
var didReceiveMemoryWarning: ControlEvent<Void>
}
複製代碼
view.rx
.tapGesture()
.when(.recognized)
.subscribe(onNext: { _ in
//react to taps
})
.disposed(by: stepBag)
複製代碼
你也能夠對多種手勢作出反應。例如,當用戶點擊或上下滑動照片預覽時,你可能想要關閉它:
view.rx
.anyGesture(.tap(), .swipe([.up, .down]))
.when(.recognized)
.subscribe(onNext: { _ in
//dismiss presented photo
})
.disposed(by: stepBag)
複製代碼
rx.gesture
被定義爲Observable<G>
其中G
是手勢識別器的實際類型因此它發出的是手勢識別器自己(若是想調用asLocation(in view:)
或asTranslation(in view:)
這樣的方法很方便)
RxGesture支持以下手勢:
view.rx.tapGesture() -> ControlEvent<UITapGestureRecognizer>
view.rx.pinchGesture() -> ControlEvent<UIPinchGestureRecognizer>
view.rx.swipeGesture(.left) -> ControlEvent<UISwipeGestureRecognizer>
view.rx.panGesture() -> ControlEvent<UIPanGestureRecognizer>
view.rx.longPressGesture() -> ControlEvent<UILongPressGestureRecognizer>
view.rx.rotationGesture() -> ControlEvent<UIRotationGestureRecognizer>
view.rx.screenEdgePanGesture() -> ControlEvent<UIScreenEdgePanGestureRecognizer>
view.rx.anyGesture(.tap(), ...) -> ControlEvent<UIGestureRecognizer>
view.rx.anyGesture(.pinch(), ...) -> ControlEvent<UIGestureRecognizer>
view.rx.anyGesture(.swipe(.left), ...) -> ControlEvent<UIGestureRecognizer>
view.rx.anyGesture(.pan(), ...) -> ControlEvent<UIGestureRecognizer>
view.rx.anyGesture(.longPress(), ...) -> ControlEvent<UIGestureRecognizer>
view.rx.anyGesture(.rotation(), ...) -> ControlEvent<UIGestureRecognizer>
view.rx.anyGesture(.screenEdgePan(), ...) -> ControlEvent<UIGestureRecognizer>
複製代碼
若是您單獨使用手勢識別器,請選擇view.rx.fooGesture()
語法而不是view.rx.anyGesture(.foo())
,由於它返回具體的UIGestureRecognizer
子類,並避免您將其轉換爲subscribe()
。
默認狀況下,手勢識別器的狀態沒有過濾器。這意味着,這裏有能夠用於各類手勢(iOS和macOS)的首選狀態:
view.rx.tapGesture().when(.recognized)
view.rx.panGesture().when(.began, .changed, .ended)
複製代碼
若是你同時觀察多個手勢,你可使用when()
操做符,若是你想過濾全部手勢識別器的相同狀態,或者使用tuple
語法進行單獨的過濾:
view.rx
.anyGesture(.tap(), .swipe([.up, .down]))
.when(.recognized)
.subscribe(onNext: { gesture in
// Called whenever a tap, a swipe-up or a swipe-down is recognized (state == .recognized)
})
.disposed(by: bag)
view.rx
.anyGesture(
(.tap(), when: .recognized),
(.pan(), when: .ended)
)
.subscribe(onNext: { gesture in
// Called whenever:
// - a tap is recognized (state == .recognized)
// - or a pan is ended (state == .ended)
})
.disposed(by: bag)
複製代碼
這裏有一個官方的演示應用程序包括全部識別器的例子: ➡️ iOS, macOS.
每一個手勢識別器都有一個默認的RxGestureRecognizerDelegate。它容許你使用一個策略自定義每一個委託方法:
.always
: 對應的委託方法是否返回true.never
: 將返回false到相應的委託方法.custom
: 獲取將執行的關聯閉包,以將值返回給相應的委託方法
如下是可用的策略及其相應的委託方法:
beginPolicy -> gestureRecognizerShouldBegin(:_)
touchReceptionPolicy -> gestureRecognizer(_:shouldReceive:)
selfFailureRequirementPolicy -> gestureRecognizer(_:shouldBeRequiredToFailBy:)
otherFailureRequirementPolicy -> gestureRecognizer(_:shouldRequireFailureOf:)
simultaneousRecognitionPolicy -> gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:)
eventRecognitionAttemptPolicy -> gestureRecognizer(_:shouldAttemptToRecognizeWith:) // macOS only
pressReceptionPolicy -> gestureRecognizer(_:shouldReceive:) // iOS only
複製代碼
這個委託能夠在配置包中定製:
view.rx.tapGesture(configuration: { gestureRecognizer, delegate in
delegate.simultaneousRecognitionPolicy = .always // (default value)
// or
delegate.simultaneousRecognitionPolicy = .never
// or
delegate.simultaneousRecognitionPolicy = .custom { gestureRecognizer, otherGestureRecognizer in
return otherGestureRecognizer is UIPanGestureRecognizer
}
delegate.otherFailureRequirementPolicy = .custom { gestureRecognizer, otherGestureRecognizer in
return otherGestureRecognizer is UILongPressGestureRecognizer
}
})
複製代碼
默認值能夠在RxGestureRecognizerDelegate.swift中找到。
您還能夠用本身的委託替換默認委託,或者刪除它。代碼以下:
view.rx.tapGesture { [unowned self] gestureRecognizer, delegate in
gestureRecognizer.delegate = nil
// or
gestureRecognizer.delegate = self
}
複製代碼
Add this to Podfile
pod "RxGesture"
複製代碼
$ pod install Carthage
Add this to Cartfile
github "RxSwiftCommunity/RxGesture" ~> 3.0
複製代碼
$ carthage update
RxSwift
擴展。除另有說明外,全部操做符也可用於驅動程序和信號。
Observable<String?>
.of("One", nil, "Three")
.filterNil()
// Type is now Observable<String>
.subscribe { print($0) }
複製代碼
結果打印:
next(One)
next(Three)
completed
複製代碼
replaceNilWith 的用法:
Observable<String?>
.of("One", nil, "Three")
.replaceNilWith("Two")
// Type is now Observable<String>
.subscribe { print($0) }
複製代碼
打印結果:
next(One)
next(Two)
next(Three)
completed
複製代碼
errorOnNil 的用法:
注意:在驅動程序上不可用,由於驅動程序不能出錯。 默認狀況下,
rxoptionalerror
.foundnilwhile
eunwrappingoptional
有錯誤。
Observable<String?>
.of("One", nil, "Three")
.errorOnNil()
// Type is now Observable<String>
.subscribe { print($0) }
複製代碼
結果打印:
next(One)
error(Found nil while trying to unwrap type <Optional<String>>)
複製代碼
catchOnNil 的用法:
Observable<String?>
.of("One", nil, "Three")
.catchOnNil {
return Observable<String>.just("A String from a new Observable")
}
// Type is now Observable<String>
.subscribe { print($0) }
複製代碼
打印結果:
next(One)
next(A String from a new Observable)
next(Three)
completed
複製代碼
distinctUntilChanged 的用法:
Observable<Int?>
.of(5, 6, 6, nil, nil, 3)
.distinctUntilChanged()
.subscribe { print($0) }
複製代碼
打印結果:
next(Optional(5))
next(Optional(6))
next(nil)
next(Optional(3))
completed
複製代碼
String
Array
Dictionary
Set
目前在Swift協議中不能擴展到符合其餘協議。目前,上面列出的類型符合Occupiable。您還可使自定義類型符合Occupiable。
filterEmpty 的用法:
Observable<[String]>
.of(["Single Element"], [], ["Two", "Elements"])
.filterEmpty()
.subscribe { print($0) }
複製代碼
打印結果:
next(["Single Element"])
next(["Two", "Elements"])
completed
複製代碼
errorOnEmpty的用法:
在驅動程序上不可用,由於驅動程序不能出錯。 默認狀況下,
RxOptionalError.emptyOccupiable
會出現錯誤。
Observable<[String]>
.of(["Single Element"], [], ["Two", "Elements"])
.errorOnEmpty()
.subscribe { print($0) }
複製代碼
打印結果:
next(["Single Element"])
error(Empty occupiable of type <Array<String>>)
複製代碼
catchOnEmpty 的用法:
Observable<[String]>
.of(["Single Element"], [], ["Two", "Elements"])
.catchOnEmpty {
return Observable<[String]>.just(["Not Empty"])
}
.subscribe { print($0) }
複製代碼
打印結果:
next(["Single Element"])
next(["Not Empty"])
next(["Two", "Elements"])
completed
複製代碼
CocoaPods
RxOptional能夠經過CocoaPods得到。要安裝它,只需將如下行添加到您的Podfile中:
pod 'RxOptional'
複製代碼
Carthage
將此添加到Cartfile
github "RxSwiftCommunity/RxOptional" ~> 4.1.0
複製代碼
$ carthage update
RxTheme基於Rx的主題管理擴展框架
pod 'RxTheme', '~> 4.0'
複製代碼
Carthage
github "RxSwiftCommunity/RxTheme" ~> 4.0.0
複製代碼
經過RxTheme 你能夠這樣定義app 的主題服務:
import RxTheme
protocol Theme {
var backgroundColor: UIColor { get }
var textColor: UIColor { get }
}
struct LightTheme: Theme {
let backgroundColor = .white
let textColor = .black
}
struct DarkTheme: Theme {
let backgroundColor = .black
let textColor = .white
}
enum ThemeType: ThemeProvider {
case light, dark
var associatedObject: Theme {
switch self {
case .light:
return LightTheme()
case .dark:
return DarkTheme()
}
}
}
let themeService = ThemeType.service(initial: .light)
複製代碼
// Bind stream to a single attribute
// In the way, RxTheme would automatically manage the lifecycle of the binded stream
view.theme.backgroundColor = themeService.attrStream { $0.backgroundColor }
// Or bind a bunch of attributes, add them to a disposeBag
themeService.rx
.bind({ $0.textColor }, to: label1.rx.textColor, label2.rx.textColor)
.bind({ $0.backgroundColor }, to: view.rx.backgroundColor)
.disposed(by: disposeBag)
複製代碼
全部由ThemeService生成的流都是共享的(1)
themeService.switch(.dark)
// When this is triggered by some signal, you can use:
someSignal.bind(to: themeService.switcher)
複製代碼
此外RxTheme還提供了下面的一些API:
// Current theme type
themeService.type
// Current theme attributes
themeService.attrs
// Theme type stream
themeService.typeStream
// Theme attributes stream
themeService.attrsStream
複製代碼
backgroundColor borderWidth borderColor shadowColor
CAShapeLayer:
strokeColor fillColor
UIActivityIndicatorView
style
UIBarButtonItem
tintColor
UIButton
titleColor
UILabel
font textColor highlightedTextColor shadowColor
UINavigationBar
barStyle barTintColor titleTextAttributes
UIPageControl
pageIndicatorTintColor currentPageIndicatorTintColor
UIProgressView
progressTintColor trackTintColor
UISearchBar
barStyle barTintColor keyboardAppearance
UISlider
thumbTintColor minimumTrackTintColor maximumTrackTintColor
UISwitch
onTintColor thumbTintColor
UITabBar
barStyle barTintColor
UITableView
separatorColor
UITAbleViewCell
selectionStyle
UITextField
font textColor keyboardAppearance
UITextView
font textColor keyboardAppearance
UIToolbar
barStyle barTintColor
UIView
tintColor
RxTheme
使用來自RxCocoa
的Binder<T>
,因此RxCocoa
中定義的任何Binder
均可以在這裏使用。 這也使得庫超級容易在你的代碼庫中擴展,下面是一個例子:extension Reactive where Base: UIView {
var borderColor: Binder<UIColor?> {
return Binder(self.base) { view, color in
view.layer.borderColor = color?.cgColor
}
}
}
複製代碼
若是您還想使用sugar view.theme
。邊界顏色,你必須寫另外一個擴展:
extension ThemeProxy where Base: UIView {
var borderColor: Observable<UIColor?> {
get { return .empty() }
set {
let disposable = newValue
.takeUntil(base.rx.deallocating)
.observeOn(MainScheduler.instance)
.bind(to: base.rx.borderColor)
hold(disposable, for: "borderColor")
}
}
}
複製代碼