private let provider = MoyaProvider<ItemAPI>(stubClosure: MoyaProvider.ImmediatelyStub) |
</tr>
</tbody>
</table>
注意這里的MoyaProvider.ImmediatelyStub,我原以為它是個枚舉類型,看了MoyaProvider定義發現這里應該傳個closure,看了ImmediatelyStub的定義發現原來它是個類方法:
</span>public typealias StubClosure = Target -> Moya.StubBehavior
override public init(stubClosure: StubClosure = MoyaProvider.NeverStub, ...) {
}
public final class func ImmediatelyStub(_: Target) -> Moya.StubBehavior {
return .Immediate
}</pre>
如果想打印每次請求的參數,在組裝endpoint的時候打印即可:
private func endpointMapping<Target: MoyaTarget>(target: Target) -> Endpoint<Target> {
if let parameters = target.parameters {
log.verbose("(parameters)")
}
return MoyaProvider.DefaultEndpointMapping(target)
}
private let provider = RxMoyaProvider<ItemAPI>(endpointClosure: endpointMapping)</pre>
RxSwift前面強行安利過兩波,在此不再贅述啦,Moya本身提供了RxSwift擴展,可以無縫銜接RxSwift和ReactiveCocoa,于是打開方式變成了這樣:
</span>private let provider = RxMoyaProvider<ItemAPI>()
private var disposeBag = DisposeBag()
extension ItemAPI {
static func getNewItems(completion: [Item] -> Void) {
disposeBag = DisposeBag()
provider
.request(.GetItems())
.subscribe(
onNext: { items in
completion(items)
}
)
.addDisposableTo(disposeBag)
}
}</pre>
Moya的核心開發者、同時也是 Artsy 的成員:Ash Furrow, 在 AltConf 做過一次 《Functional Reactive Awesomeness With Swift》 的分享,推薦大家看一下,很可愛的!
Argo是thoughtbot開源的函數式JSON解析轉換庫。說到thoughtbot就不得不提他司關于JSON解析質量很高的一系列文章:
struct Item {
let id: String
let url: String
}
extension Item: Decodable {
static func decode(j: JSON) -> Decoded<Item> {
return curry(Item.init)
<^> j <| "id"
<*> j <| "url"
}
}</pre> 至于這其中各種符號的緣由,在幾篇博客中都有講解,還是挺有意思滴。
All
說完這三者,如何把它們串起來呢?Emergence 中的 Observable/Networking 給了我們答案。稍微整理后如下:
</span>enum ORMError : ErrorType {
case ORMNoRepresentor
case ORMNotSuccessfulHTTP
case ORMNoData
case ORMCouldNotMakeObjectError
}
extension Observable {
private func resultFromJSON<T: Decodable>(object:[String: AnyObject], classType: T.Type) -> T? {
let decoded = classType.decode(JSON.parse(object))
switch decoded {
case .Success(let result):
return result as? T
case .Failure(let error):
log.error("(error)")
return nil
}
}
func mapSuccessfulHTTPToObject<T: Decodable>(type: T.Type) -> Observable<T> {
return map { representor in
guard let response = representor as? MoyaResponse else {
throw ORMError.ORMNoRepresentor
}
guard ((200...209) ~= response.statusCode) else {
if let json = try? NSJSONSerialization.JSONObjectWithData(response.data, options: .AllowFragments) as? [String: AnyObject] {
log.error("Got error message: \(json)")
}
throw ORMError.ORMNotSuccessfulHTTP
}
do {
guard let json = try NSJSONSerialization.JSONObjectWithData(response.data, options: .AllowFragments) as? [String: AnyObject] else {
throw ORMError.ORMCouldNotMakeObjectError
}
return self.resultFromJSON(json, classType:type)!
} catch {
throw ORMError.ORMCouldNotMakeObjectError
}
}
}
func mapSuccessfulHTTPToObjectArray<T: Decodable>(type: T.Type) -> Observable<[T]> {
return map { response in
guard let response = response as? MoyaResponse else {
throw ORMError.ORMNoRepresentor
}
// Allow successful HTTP codes
guard ((200...209) ~= response.statusCode) else {
if let json = try? NSJSONSerialization.JSONObjectWithData(response.data, options: .AllowFragments) as? [String: AnyObject] {
log.error("Got error message: \(json)")
}
throw ORMError.ORMNotSuccessfulHTTP
}
do {
guard let json = try NSJSONSerialization.JSONObjectWithData(response.data, options: .AllowFragments) as? [[String : AnyObject]] else {
throw ORMError.ORMCouldNotMakeObjectError
}
// Objects are not guaranteed, thus cannot directly map.
var objects = [T]()
for dict in json {
if let obj = self.resultFromJSON(dict, classType:type) {
objects.append(obj)
}
}
return objects
} catch {
throw ORMError.ORMCouldNotMakeObjectError
}
}
}
}</pre>
這樣在調用的時候就很舒服了,以前面的Item為例:
private let provider = RxMoyaProvider<ItemAPI>()
private var disposeBag = DisposeBag()
extension ItemAPI {
static func getNewItems(records:[Record] = [], needCount: Int, completion: [Item] -> Void) {
disposeBag = DisposeBag()
provider
.request(.AddRecords(records, needCount))
.mapSuccessfulHTTPToObjectArray(Item)
.subscribe(
onNext: { items in
completion(items)
}
)
.addDisposableTo(disposeBag)
}
}</pre>
一個mapSuccessfulHTTPToObjectArray方法,直接將JSON字符串轉換成了Item對象,并且傳入了后面的數據流中,所以在onNext訂閱的時候傳入的就是[Item]數據,并且這個轉換過程還是可以復用的,且適用于所有網絡請求中JSON和Model的轉換。爽就一個字,我只說一次。
爽!
Next
匆匆讀了一點 Emergence 和 Eidolon 的項目源碼,沒有深入不過已經受益匪淺。通過 bundle 管理 id 和 key 直接解決了我當初糾結已久的『完整項目開源如何優雅地保留 git 記錄且保護項目隱私』的問題,還有Moya/RxSwift和Moya/ReactiveCocoa這種子模塊化處理也在共有模塊管理這個問題上給了我一些啟發。
真是很喜歡 Artsy 這樣的團隊,大家都一起做著自己喜歡的事情,還能站著把錢賺了。
所幸的是我也可以這樣做自己喜歡的事情了,不過不賺錢。具體狀況后面單獨開一篇閑扯扯。
碎告。
參考資料: