swift - 链式网络请求 RXSwift
问题描述
嘿,我在 HackingWithSwift 教程中学习了如何使用 Combine 发出链式网络请求(参见下面的代码)。现在我将使用 RXSwift 构建相同的逻辑,但我不知道如何像在 Combine 中那样获取/订阅以获得最终结果。
结合:
//Combine code
func fetch<T: Decodable>(_ url: URL, defaultValue: T) -> AnyPublisher<T, Never> {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
return URLSession.shared.dataTaskPublisher(for: url)
.retry(1)
.map(\.data)
.decode(type: T.self, decoder: decoder)
.replaceError(with: defaultValue)
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
//call fetch method and get the end result
fetch(url, defaultValue: [URL]())
.flatMap { urls in
urls.publisher.flatMap { url in
fetch(url, defaultValue: [NewsItem]())
}
}
.collect()
.sink { values in
let allItems = values.joined()
items = allItems.sorted { $0.id > $1.id }
}
.store(in: &requests)
//RXSwift code
func fetchWithRX<T: Decodable>(_ url: URL, defaultValue: T) -> Observable<T> {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let request = URLRequest(url: url)
return URLSession.shared.rx.response(request: request)
.retry(1)
.map(\.data)
.decode(type: T.self, decoder: decoder)
.debug()
.catchAndReturn(defaultValue)
.observe(on: MainScheduler.instance)
}
//call fetch2 method
Now I want to subscribe to the values like in the first fetch method with flatMap..collect..sink etc.
fetchWithRX(url, defaultValue: [URL]())
解决方案
我会这样写模拟:
fetchWithRX(url, defaultValue: [URL]())
.flatMap { urls in
Observable.zip(urls.map { fetchWithRX($0, defaultValue: [NewsItem]()) })
}
.map { $0.flatMap { $0 }.sorted { $0.id > $1.id } }
.subscribe(onNext: { values in
items = values
})
.disposed(by: requests)
这样,我将所有逻辑移动到一个map
闭包中,该闭包可以移动到一个函数中以实现可测试性。尽量减少 a flatMap
orsubscribe
中的代码量,以提高代码的可测试性。
或者你可以这样写:
fetchWithRX(url, defaultValue: [URL]())
.flatMap { urls in
Observable.zip(urls.map { fetchWithRX($0, defaultValue: [NewsItem]()) })
}
.subscribe(onNext: { values in
let allItems = values.joined()
items = allItems.sorted { $0.id > $1.id }
})
.disposed(by: requests)
你可以在这篇文章中了解更多关于组合 observables 的信息:Recipes for Combining Observables in RxSwift
URLSession 还有一个操作符data(request:)
,它只会发出数据,因此您不必映射来转储结果对象。像这样:
func fetchWithRX<T: Decodable>(_ url: URL, defaultValue: T) -> Observable<T> {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
return URLSession.shared.rx.data(request: URLRequest(url: url))
.retry(1)
.decode(type: T.self, decoder: decoder)
.catchAndReturn(defaultValue)
.observe(on: MainScheduler.instance)
}
我突然想到,您可能正在寻找原始组合代码的直接推论......上述示例将具有相同的最终输出,但它们的工作方式略有不同......
这是直接翻译:
fetchWithRX(url, defaultValue: [URL]())
.flatMap { urls in
// Observable.from(urls) works like urls.publisher
Observable.from(urls).flatMap { url in
fetchWithRX(url, defaultValue: [NewsItem]())
}
}
.toArray() // works like collect(). However, toArray() returns a Single rather than a generic Observable.
.subscribe(onSuccess: { values in
let allItems = values.joined()
items = allItems.sorted { $0.id > $1.id }
})
.disposed(by: requests)
不同之处在于其他示例保留了新闻项目的顺序,而这没有。由于无论如何您都在收集和排序,因此最终输出是相同的。如果您在观察输出之前没有使用collect()
/ ,您只会看到差异。toArray()
推荐阅读
- excel - 选择其他活动单元格后保持第一行格式
- mysql - 如何根据外键对每一行具有不同状态的状态从父表和子表中获取数据
- c# - 正确使用存储库模式。应该返回多种实体类型还是只返回一种?
- jbpm - 属性 org.appformer.m2repo.url 未正确设置
- javascript - D3.js:xScale.bandwith 不是函数
- javascript - 从对象数组Javascript计算2个对象键/列
- c - 在 Xcode 11.x 中设置 C 项目
- python - 为 pandas 中的每个数据框返回自定义变量
- sql-server - 在“as”附近出现动态枢轴错误
- r - 如何维护 R (renv) 项目