我有一个返回可观察结果的网络请求,我只想将错误消息作为字符串捕获,以便我可以将错误消息与另一条错误消息合并。我尝试使用结果中的 map 仅映射出错误,但我无法弄清楚如何正确捕获错误。
这是我的网络管理员
class NetworkManager {
private let baseURL = "https://api.github.com/"
func getFollowers(with username: String, page: Int) -> Observable<Result<[Follower], GFError>> {
let endpoint = baseURL + "users/\(username)/followers?per_page=100&page=\(page)"
let url = URL(string: endpoint)!
return Observable.create { (observer) -> Disposable in
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let _ = error {
observer.onNext(.failure(.unableToComplete))
return
}
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
observer.onNext(.failure(.invalidResponse))
return
}
guard let data = data else {
observer.onNext(.failure(.invalidData))
return
}
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
decoder.dateDecodingStrategy = .iso8601
let results = try decoder.decode([Follower].self, from: data)
observer.onNext(.success(results))
observer.onCompleted()
} catch {
observer.onNext(.failure(.invalidData))
}
}
task.resume()
return Disposables.create {
task.cancel()
}
}
}
这是我的视图模型
protocol ViewModelType {
associatedtype Input
associatedtype Output
func transform(input: Input) -> Output
}
class SearchViewModel: ViewModelType {
// MARK: Properties
let manager: NetworkManager
// MARK: Binding
struct Input {
let searchText: Observable<String>
let validate: Observable<Void>
}
struct Output {
let followers: Driver<Result<[FollowerViewModel], GFError>>
let errorMessage: Driver<String>
}
init(manager: NetworkManager) {
self.manager = manager
}
func transform(input: Input) -> Output {
let followers = input.validate
.withLatestFrom(input.searchText)
.filter { !$0.isEmpty }
.flatMapLatest { query in
return self.manager.getFollowers(with: query, page: 1)
}.asDriver { error in
return Driver.just(.failure(error as! GFError))
}
let missingName = input.validate
.withLatestFrom(input.searchText)
.compactMap { $0.isEmpty ? "Please enter a username. We need to know who to look for" : nil }
.asDriver(onErrorJustReturn: "")
let errorMessage = followers
let followerVM = followers.map { $0.map { $0.map { FollowerViewModel(follower: $0) }}}
return Output(followers: followerVM, errorMessage: errorMessage)
}
}
答案 0 :(得分:1)
您的视图模型非常强大,只有一些小错误。您的网络请求返回 Observable Result 的事实意味着您需要使用紧凑地图和 case let 来提取值。有一些库可以使这更容易。一种叫做 RxEnumKit。
class SearchViewModel {
// MARK: Properties
let manager: NetworkManager
// MARK: Binding
struct Input {
let searchText: Observable<String>
let validate: Observable<Void>
}
struct Output {
let followers: Driver<[FollowerViewModel]> // note, this should not return a Driver-result.
let errorMessage: Driver<String>
}
init(manager: NetworkManager) {
self.manager = manager
}
func transform(input: Input) -> Output {
let followers = input.validate
.withLatestFrom(input.searchText)
.filter { !$0.isEmpty }
.flatMapLatest { query in
return self.manager.getFollowers(with: query, page: 1)
}
let missingName = input.validate
.withLatestFrom(input.searchText)
.compactMap { $0.isEmpty ? "Please enter a username. We need to know who to look for" : nil }
// the below extracts the error string from the followers observable and merges it with the missingName observable to make the errorMessage observable.
let errorMessage = Observable.merge(
missingName,
followers.compactMap { (result) -> String? in
guard case let .failure(error) = result else { return nil }
return error.localizedDescription
}
)
.asDriver(onErrorJustReturn: "")
// use the same thing here to pull the followers out of the Result.
let followerVM = followers.compactMap { (result) -> [FollowerViewModel]? in
guard case let .success(followers) = result else { return nil }
return followers.map { FollowerViewModel(follower: $0) }
}
.asDriver(onErrorJustReturn: [])
return Output(followers: followerVM, errorMessage: errorMessage)
}
}