Networking Layer in Swift , Completion Blocks and Errors
我正在用 Swift 实现一个网络层。这是功能之一。该功能按预期工作,但我想改进它。我正在使用 DispatchQueue 来确保来自网络客户端的回调始终在主线程上。这最终会在 3 个不同的地方重复 DispatchQueue.main.async。
此外,当我在执行请求时遇到一些错误时,我仍然会返回 nil 但作为成功。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | func getAllStocks(url: String, completion: @escaping (Result<[Stock]?,NetworkError>) -> Void) { guard let url = URL(string: url) else { completion(.failure(.invalidURL)) // wrap in DispatchQueue also return } URLSession.shared.dataTask(with: url) { data, response, error in guard let data = data, error == nil else { DispatchQueue.main.async { completion(.success(nil)) // should I send nil or some sort of failure } return } let stocks = try? JSONDecoder().decode([Stock].self, from: data) DispatchQueue.main.async { completion(.success(stocks)) } } } |
我怎样才能最小化代码或者它可以吗?
我建议在当前线程上调用
同时处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | func getAllStocks(url: String, completion: @escaping (Result<[Stock],Error>) -> Void) { guard let url = URL(string: url) else { completion(.failure(NetworkError.invalidURL)) return } URLSession.shared.dataTask(with: url) { data, response, error in if let error = error { completion(.failure(error)); return } // if error is nil then data has a value do { let stocks = try JSONDecoder().decode([Stock].self, from: data!) completion(.success(stocks)) } catch { completion(.failure(error)) } }.resume() } |
1 2 3 4 5 6 7 8 9 10 | getAllStocks(url: someURL) { result in DispatchQueue.main.async { switch result { case .success(let stocks): print(stocks) case .failure(let networkError as NetworkError): handleNetworkError(networkError) case .failure(let decodingError as DecodingError): handleDecodingError(decodingError) case .failure(let error): print(error) } } } |
了解内置结构和标准类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | func getAllStocks(url: String, completion: @escaping (Result<[Stock], Error>) -> Void) { func completeOnMain(_ result: Result<[Stock], Error>) { // <-- Nested function DispatchQueue.main.async { completion(result) } // <-- Handle repeated work } guard let url = URL(string: url) else { completeOnMain(.failure(URLError(.badURL))) // <-- Standard Error return } URLSession.shared.dataTask(with: url) { data, response, error in do { if let error = error { throw error } guard let data = data else { throw URLError(.badServerResponse) } let stocks = try JSONDecoder().decode([Stock].self, from: data) completeOnMain(.success(stocks)) } catch { completeOnMain(.failure(error)) // <-- Unified error handling } } } |
- 嵌套函数用于重复调度到主线程的工作。
- 使用标准错误而不是定义自定义错误。
- do/catch 和 throws 用于一次处理所有错误。
我有最后一点:异步函数应该始终是异步的。 bad URL错误不应该直接调用