关于使用Result<T>回调的Swift网络请求函数的技术咨询
Hey there! Let's dive into your Swift network request code with the Result<T> type—we'll polish up the implementation, beef up error handling, and fix a few easy-to-miss issues. First, here's a refined version of your code, then we'll break down all the improvements:
Optimized & Robust Implementation
// Custom error enum for type-safe, descriptive network errors enum NetworkError: Error, LocalizedError { case invalidURL case requestFailed(Error) case invalidResponse case invalidStatusCode(Int) case noData case decodingFailed(Error) var errorDescription: String? { switch self { case .invalidURL: return NSLocalizedString("Error 100: Invalid URL format", comment: "Invalid URL error message") case .requestFailed(let error): return NSLocalizedString("Request failed: \(error.localizedDescription)", comment: "Request failure message") case .invalidResponse: return NSLocalizedString("Server returned an invalid response", comment: "Invalid response message") case .invalidStatusCode(let code): return NSLocalizedString("Server error: Code \(code)", comment: "Status code error message") case .noData: return NSLocalizedString("Server returned no data", comment: "No data error message") case .decodingFailed(let error): return NSLocalizedString("Failed to parse data: \(error.localizedDescription)", comment: "Decoding failure message") } } } // Fixed typo and updated Result to use our custom error enum Result<T> { case success(T) case failure(NetworkError) } // Swift-style naming for typealiases (camelCase for variables, PascalCase for types) typealias CompletionHandler<T> = (Result<T>) -> Void // Added dependency injection for testability, plus full error coverage func getJsonFromServer(parameters: String, session: URLSession = .shared, completion: @escaping CompletionHandler<Data>) { let fullUrlString = ApiConstants.fullPath + parameters // Validate URL first guard let url = URL(string: fullUrlString) else { return completion(.failure(.invalidURL)) } session.dataTask(with: url) { data, response, error in // Handle request-level errors first if let error = error { return completion(.failure(.requestFailed(error))) } // Ensure we're dealing with an HTTP response guard let httpResponse = response as? HTTPURLResponse else { return completion(.failure(.invalidResponse)) } // Check for successful status codes (2xx range) guard 200..<300 ~= httpResponse.statusCode else { return completion(.failure(.invalidStatusCode(httpResponse.statusCode))) } // Make sure we have data to work with guard let data = data else { return completion(.failure(.noData)) } // All checks passed—return the data completion(.success(data)) }.resume() // Critical: Don't forget to start the task! } // Corrected spelling of ApiConstants (PascalCase for structs) struct ApiConstants { static let fullPath = "https://your-api-base-url.com/" }
Key Improvements & Error Handling Best Practices
Let's break down what changed and why:
- Fixed Naming & Typos: Corrected
succes→success,ApiConstans→ApiConstants, and used Swift's standard PascalCase for types/structs. This makes your code more readable and consistent with community standards. - Type-Safe Custom Errors: Replaced string-based errors with a
NetworkErrorenum. This eliminates ambiguous error messages, makes error handling more intentional (you can switch on specific error types), and supports localized user-facing messages viaLocalizedError. - Comprehensive Error Coverage: We now handle every possible failure point:
- Invalid URL format
- Network request failures (timeouts, connectivity issues, etc.)
- Non-HTTP responses (e.g., FTP or local file responses)
- Non-success status codes (4xx client errors, 5xx server errors)
- Empty data returned from the server
- Testability with Dependency Injection: Added a
sessionparameter (defaulting toURLSession.shared) so you can pass a mock or ephemeral session for unit testing—no more relying on live network calls in tests! - Critical
resume()Call: Your original code missed this—URLSession.dataTaskcreates a task but doesn't start it. Forgettingresume()is one of the most common network request bugs.
Bonus: Async/Await Alternative (Swift 5.5+)
If you're targeting iOS 15+/macOS 12+, using async/await cleans up the code even more and eliminates callback hell:
func getJsonFromServer(parameters: String, session: URLSession = .shared) async throws -> Data { let fullUrlString = ApiConstants.fullPath + parameters guard let url = URL(string: fullUrlString) else { throw NetworkError.invalidURL } let (data, response) = try await session.data(from: url) guard let httpResponse = response as? HTTPURLResponse else { throw NetworkError.invalidResponse } guard 200..<300 ~= httpResponse.statusCode else { throw NetworkError.invalidStatusCode(httpResponse.statusCode) } return data }
Bonus: Generic Decoding Extension
If you regularly decode JSON into models, add this generic function to skip manual decoding steps:
func fetchAndDecode<T: Decodable>(parameters: String, session: URLSession = .shared, completion: @escaping CompletionHandler<T>) { getJsonFromServer(parameters: parameters, session: session) { result in switch result { case .success(let data): do { let model = try JSONDecoder().decode(T.self, from: data) completion(.success(model)) } catch { completion(.failure(.decodingFailed(error))) } case .failure(let error): completion(.failure(error)) } } }
内容的提问来源于stack exchange,提问作者Łukasz Betta
相关产品推荐
相关产品推荐

