【问题标题】:in Swift Combine how to use combine latest or catch to check error?在 Swift Combine 中如何使用 combine latest 或 catch 来检查错误?
【发布时间】:2021-11-22 08:44:36
【问题描述】:

我有一个流程,我应该从 Web 获取帖子,并将它们保存在用户默认值上作为测试,如果有问题,从用户默认值而不是 Web 加载列表。现在我有两个发射器,我想我应该在我的存储库中执行检查,例如如果门柱发出错误,则决定显示本地缓存的帖子。如何捕捉错误?他们告诉我我可以使用 combine latest 或 mergeMany 但我是全新的,不知道如何执行此检查

我有这个提示,但不起作用

dataSource.getPosts().catch {sharedPreferenceDataSource.getLocalPosts()}

这似乎有效,但仍然没有出错或使用最新的组合

return dataSource.getPosts().catch { error in
    return self.sharedPreferenceDataSource.getLocalPosts()
}.eraseToAnyPublisher()

我的功能

 func getPosts() -> AnyPublisher<[Post], Error> {
        
        //posts from web
        dataSource.getPosts() //emits AnyPublisher<[Post], Error>
        //posts on usersDeafault
        sharedPreferenceDataSource.getLocalPosts() //emits AnyPublisher<[Post], Error>
        
        //here someone told me I should perform a catch,I get something likeMyEnumeError.networkError, then I should emit local posts
        
        //my latest attempt
        return Publishers.CombineLatest(dataSource.getPosts(), sharedPreferenceDataSource.getLocalPosts())

//        but I think I could do something like
//        perform a catch on the error from network, in that case perform a catch and send local data
//        Publishers.MergeMany([dataSource.getPosts(),dataSource.getLocalPosts(defaults: defaults, key: UserDefaultKeys.allPost.rawValue)])
}

我的电话

enum NetworkError: Error {
    case genericError(code: Int)
    case invalidResponseCode
    case decodingFailure(reason: String)
}

func getPosts() -> AnyPublisher<[Post], Error> {


let session = URLSession.shared
let url = URL(string: "https://jsonplaceholder.typicode.com/posts")!


return Future<[Post], Error>() { promise in
    
    //***********************************************
    let task = session.dataTask(with: url, completionHandler: { data, response, error in
        
        if error != nil {
            print(error ?? "N/D")
            promise(Result.failure(NetworkError.invalidResponseCode))
            return
        }
        
        
        guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
            promise(Result.failure(NetworkError.invalidResponseCode))
            return
        }
        
        do {
            let posts = try JSONDecoder().decode([Post].self, from: data! )
            promise(Result.success(posts))
            //saving on local disk
            self.saveLocalPosts(defaults: self.defaults, data: AllPosts(Posts: posts))
        } catch {
            print("Error during JSON serialization: \(error.localizedDescription)")
            promise(Result.failure(NetworkError.decodingFailure(reason: error.localizedDescription)))
                
        }
        
    })
    task.resume()
    //***********************************************

}
//            .receive(on: DispatchQueue.main)
//            .subscribe(on: DispatchQueue.init(label: "test", qos: .default))
//            .map { value -> [Post] in
//                let c = value
//                return []
//            }
.eraseToAnyPublisher()


 
}

【问题讨论】:

  • CombineLatest 运算符结合了两个独立的源。但在您的示例中,本地任务取决于远程任务的输出。所以mapflatMap 可能更合适。
  • 嗨,瓦迪安!起初我需要使用 combineLatest 和/或使用我现在添加到问题中的“提示”,问题是如何使用该提示执行错误检查
  • 更准确地说,使用tryMap 运算符能够捕获错误。
  • 如果我尝试使用 catch 或 tryMap 我无论如何都无法获得错误类型,例如使用开关。

标签: swift combine combinelatest


【解决方案1】:

我的建议是创建一个函数来处理所有网络错误并返回收到的Data 或网络错误。

enum NetworkError: Error {
    case urlError(Error)
    case invalidResponseCode
    case decodingFailure(reason: String)
}

func dataTask(url: URL) -> AnyPublisher<Data, NetworkError> {
    let request = URLRequest(url: url)
    
    return URLSession.DataTaskPublisher(request: request, session: .shared)
        .tryMap { data, response in
            guard let httpResponse = response as? HTTPURLResponse, 200..<300 ~= httpResponse.statusCode else {
                throw NetworkError.invalidResponseCode
            }
            return data
        }
        .mapError { error in
            if let error = error as? NetworkError {
                return error
            } else {
                return NetworkError.urlError(error)
            }
        }
        .eraseToAnyPublisher()
} 

然后调用replaceError操作符从UserDefaults加载数据(假设它与远程数据具有相同的JSON结构)。

var subscriptions = Set<AnyCancellable>()

dataTask(url: url)
    .replaceError(with: UserDefaults.standard.data(forKey: "posts") ?? Data() )
    .decode(type: [Post].self, decoder: JSONDecoder())
    .sink(receiveCompletion: { completion in
        print(completion)
    }, receiveValue: { value in
        print(value)
    })
    .store(in: &subscriptions)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-10
    相关资源
    最近更新 更多