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")
}
}
}
复制代码