我正在 Swift 中实现一个网络层。这是函数之一。该功能按预期工作,但我想改进它。我正在使用 DispatchQueue 来确保来自网络客户端的回调始终在主线程上。这最终会在 3 个不同的地方重复 DispatchQueue.main.async。
此外,当我在执行请求时遇到一些错误时,我仍然发回 nil 但成功。
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))
}
}
}
如何最小化代码或没问题?
Result
类型的目标是在成功时返回非可选类型,在失败时返回错误。
我建议在当前线程上调用completion
并在调用方端调度结果。
并处理DecodingError
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()
}
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)
}
}
}
倾向于内置构造和标准类型。
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和抛出用于一次处理所有错误。
我还有最后一点:异步函数应该始终是异步的。错误的 URL 错误不应直接调用completion(_:)
;使用DispatchQueue.main.async
确保调用在以后的运行循环中发生。