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

Swift解析JSON响应时获取嵌套字段到Label遇解包崩溃问题

Fixing the Forced Unwrap Crash & Parsing Your Biller Response in Swift

Hey there, that crash is a classic case of unsafe force unwrapping biting you—let's break down what's going wrong and fix it properly, plus get those values into your labels.

What's Causing the Crash?

Your line let billerDetails = json["billerResponse"] as! String uses as!, which crashes if json["billerResponse"] is either missing from the response or isn't a String type. Even though your sample JSON shows it's a stringified JSON object, we should never rely on force unwraps for network responses—servers can send unexpected data!

Complete Safe Implementation

Here's a revised billerFetchService method that handles all edge cases, parses the nested JSON correctly, and updates your labels safely:

func billerFetchService() {
    // Replace with your actual API endpoint
    guard let apiUrl = URL(string: "https://your-biller-api.com/fetch-bill") else {
        print("Invalid API URL")
        return
    }
    
    // Set up POST request
    var request = URLRequest(url: apiUrl)
    request.httpMethod = "POST"
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    
    // Add your request parameters (adjust as needed for your API)
    let requestParams: [String: Any] = [
        "serviceNumber": "116515M025007621", // Example from your sample data
        // Add other required params here
    ]
    
    // Encode request body
    do {
        request.httpBody = try JSONSerialization.data(withJSONObject: requestParams, options: [])
    } catch {
        print("Failed to encode request body: \(error.localizedDescription)")
        return
    }
    
    // Execute the request
    let task = URLSession.shared.dataTask(with: request) { [weak self] data, response, error in
        guard let self = self else { return }
        
        // Handle network errors
        if let error = error {
            print("Network error occurred: \(error.localizedDescription)")
            return
        }
        
        // Validate response and data
        guard let responseData = data,
              let httpResponse = response as? HTTPURLResponse,
              httpResponse.statusCode == 200 else {
            print("Invalid response or no data received")
            return
        }
        
        // Parse outer JSON response
        do {
            guard let outerJson = try JSONSerialization.jsonObject(with: responseData) as? [String: Any],
                  let responseDict = outerJson["response"] as? [String: Any] else {
                print("Failed to parse outer response structure")
                return
            }
            
            // Safely get the billerResponse string (NO force unwrap!)
            guard let billerResponseString = responseDict["billerResponse"] as? String else {
                print("billerResponse is missing or not a string")
                return
            }
            
            // Parse the nested JSON string inside billerResponse
            guard let billerResponseData = billerResponseString.data(using: .utf8) else {
                print("Failed to convert billerResponse string to data")
                return
            }
            
            let billerDetails = try JSONSerialization.jsonObject(with: billerResponseData) as? [String: Any]
            
            // Update labels ON MAIN THREAD (UI changes must happen here!)
            DispatchQueue.main.async {
                // Use nil coalescing to show a default if fields are missing
                self.customerNameLabel.text = billerDetails?["customerName"] as? String ?? "Unknown"
                self.amountLabel.text = {
                    if let amount = billerDetails?["amount"] as? Int {
                        return String(format: "₹%.2f", Double(amount)) // Adjust currency symbol as needed
                    }
                    return "0.00"
                }()
                self.dueDateLabel.text = billerDetails?["dueDate"] as? String ?? "No due date"
                self.billNumberLabel.text = billerDetails?["billNumber"] as? String ?? "No bill number"
                self.serviceNumberLabel.text = billerDetails?["fieldValue"] as? String ?? "No service number"
                self.billerNameLabel.text = billerDetails?["billerName"] as? String ?? "Unknown biller"
            }
            
        } catch {
            print("Parsing error: \(error.localizedDescription)")
        }
    }
    
    task.resume()
}

Key Fixes & Best Practices:

  • Safe Optional Handling: Replaced all as! with guard let and optional bindings to avoid crashes if data is missing or malformed.
  • Double Parsing: Since billerResponse is a stringified JSON object, we first convert it to Data, then parse it into a dictionary.
  • Main Thread UI Updates: Always update UI elements (like labels) on the main thread using DispatchQueue.main.async—this prevents weird UI glitches or crashes.
  • Graceful Fallbacks: Used ?? to provide default values if any field is missing, so your app doesn't show empty labels.
  • Error Logging: Added detailed error messages to help you debug if something goes wrong with the network or parsing.

Bonus: Cleaner Parsing with Codable

For a more maintainable solution as your app grows, use Swift's Codable protocol instead of manual dictionary parsing. Here's how:

1. Define Codable Models

// Outer response structure
struct APIResponse: Codable {
    let response: ResponseContent
}

struct ResponseContent: Codable {
    let responseReason: String
    let billDetails: String?
    let billerResponse: String
}

// Biller details model
struct BillerDetails: Codable {
    let customerName: String
    let amount: Int
    let dueDate: String
    let billNumber: String
    let fieldName: String
    let fieldValue: String
    let billerName: String
    // Add other fields you need, mark as optional if they might be missing
}

2. Updated Parsing Logic

// Inside the data task completion handler:
do {
    // Parse outer response
    let apiResponse = try JSONDecoder().decode(APIResponse.self, from: responseData)
    
    // Parse nested billerResponse string
    guard let billerResponseData = apiResponse.response.billerResponse.data(using: .utf8) else {
        print("Failed to convert billerResponse to data")
        return
    }
    let billerDetails = try JSONDecoder().decode(BillerDetails.self, from: billerResponseData)
    
    // Update labels on main thread
    DispatchQueue.main.async {
        self.customerNameLabel.text = billerDetails.customerName
        self.amountLabel.text = String(format: "₹%.2f", Double(billerDetails.amount))
        self.dueDateLabel.text = billerDetails.dueDate
        self.billNumberLabel.text = billerDetails.billNumber
        self.serviceNumberLabel.text = billerDetails.fieldValue
        self.billerNameLabel.text = billerDetails.billerName
    }
} catch {
    print("Codable parsing error: \(error)")
}

This is much cleaner and less error-prone—you'll get compile-time checks if you misspell field names, and adding new fields is just a matter of updating the model.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:45:30