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

Swift 5中使用Decodable链式发起JSON请求的最优方案问询

解决链式网络请求的结构优化与解析错误问题

Hey there! Let's tackle your two main issues: structuring chained API requests properly and fixing that second JSON parsing error.


一、请求逻辑的结构选择:拆分复用优于重复代码

First off, never duplicate network request logic—it's messy, hard to maintain, and increases the chance of bugs. Instead of copying the URLSession code into a separate function or cramming everything into one block, you should:

  1. Create a reusable generic network request function that handles decoding any Codable model.
  2. Call this function sequentially: first for the initial search, then for each retrieved link.

This keeps your code clean, testable, and easy to debug.


二、修复第二个请求的解析错误(以及隐藏的逻辑问题)

Looking at your current code, there are a few key issues causing the parsing error and unexpected behavior:

You're always using collectLink[0] to build the URL, but if your first request returns multiple links, you'll repeatedly request the same first link instead of each unique one. Worse, if the array is empty when mainParse() runs (race condition from async tasks), you'll crash with an index out of bounds error.

Fix: Pass the retrieved validLink directly to your second request function instead of relying on an array index.

2. Mismatched Codable model vs. JSON response

The "Parse Error" in the second request almost always means your JSONResponse struct doesn't match the actual JSON structure being returned. To debug this:

  • Add a print statement inside the data task to log the raw JSON string:
    if let rawJson = String(data: data, encoding: .utf8) {
        print("Raw second response JSON: \(rawJson)")
    }
    
  • Compare this raw JSON to your JSONResponse and associated structs (like the type of locations elements). Check for:
    • Case mismatches (e.g., JSON uses rid but your model has Rid)
    • Missing optional markers (e.g., a JSON field that can be null but your model uses a non-optional type)
    • Wrong property types (e.g., JSON returns a string but your model expects an integer)

3. Unnecessary array storage (unless you need it later)

If you don't need to keep all the links for future use, you don't even need the collectLink array—just pass each link directly to the second request as you parse it.


优化后的代码示例

Here's a refactored version of your code that fixes these issues:

Step 1: Create a reusable generic network request function

func fetchAndDecode<T: Codable>(urlString: String, completion: @escaping (Result<T, Error>) -> Void) {
    guard let url = URL(string: urlString) else {
        completion(.failure(NSError(domain: "Invalid URL", code: -1, userInfo: nil)))
        return
    }
    
    let task = URLSession.shared.dataTask(with: url) { data, response, error in
        if let error = error {
            completion(.failure(error))
            return
        }
        
        guard let data = data else {
            completion(.failure(NSError(domain: "No data received", code: -2, userInfo: nil)))
            return
        }
        
        do {
            let decoded = try JSONDecoder().decode(T.self, from: data)
            completion(.success(decoded))
        } catch {
            completion(.failure(error))
        }
    }
    task.resume()
}

Step 2: Update the search bar handler

func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
    searchBar.resignFirstResponder()
    
    guard let searchText = searchController.searchBar.text, !searchText.isEmpty else {
        return
    }
    
    // Encode the search text to avoid URL issues (critical for special characters!)
    guard let encodedSearchText = searchText.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
        return
    }
    
    let initialUrl = "http://djp-dev/api/item?q=\(encodedSearchText)&dev=1"
    
    fetchAndDecode(urlString: initialUrl) { [weak self] (result: Result<Response, Error>) in
        guard let self = self else { return }
        
        switch result {
        case .success(let response):
            for result in response.results {
                if let validLink = result.link {
                    // Request each link immediately as we parse it
                    self.fetchDetails(for: validLink)
                }
            }
        case .failure(let error):
            print("Initial request error: \(error.localizedDescription)")
        }
    }
}

Step 3: Create a dedicated function for the second request

func fetchDetails(for link: String) {
    let detailUrl = "http://djp-dev\(link)?dev=1"
    
    fetchAndDecode(urlString: detailUrl) { [weak self] (result: Result<JSONResponse, Error>) in
        guard let self = self else { return }
        
        switch result {
        case .success(let response):
            // Process your locations data here
            for location in response.locations {
                print("""
Manufacture = \(location.rid)
Description = \(location.description ?? "No description")
""")
            }
            
            // Update UI on main thread
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
        case .failure(let error):
            print("Detail request error: \(error.localizedDescription)")
            // Optional: Print the decoding error details for debugging
            if let decodingError = error as? DecodingError {
                print("Decoding error details: \(decodingError)")
            }
        }
    }
}

Key improvements in this code:

  • Reusability: The fetchAndDecode function handles all network and decoding logic, so you don't repeat code.
  • Safety: Uses [weak self] to avoid retain cycles, safely unwraps optional values, and encodes the search text to prevent URL invalidity.
  • Clarity: Separates concerns into dedicated functions for search handling and detail fetching.
  • Debuggability: Provides specific error messages and logs decoding errors to help you fix the JSON model mismatch.

内容的提问来源于stack exchange,提问作者Dewan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 06:46:41