You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于使用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 NetworkError enum. This eliminates ambiguous error messages, makes error handling more intentional (you can switch on specific error types), and supports localized user-facing messages via LocalizedError.
  • 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 session parameter (defaulting to URLSession.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.dataTask creates a task but doesn't start it. Forgetting resume() 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 03:51:07