关于 ios:Swift 中的网络层、完成块和错误

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))
        }

    }

}

我怎样才能最小化代码或者它可以吗?


Result 类型的目标是在成功时返回非可选类型,在失败时返回错误。

我建议在当前线程上调用 completion 并在调用方发送结果。

同时处理 DecodingError

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错误不应该直接调用completion(_:);使用 DispatchQueue.main.async 确保调用发生在以后的运行循环中。