Swift调用API遇无数据时应用崩溃问题求助
Hey there! Let's figure out why your app is crashing when the API returns no data. I've seen this exact issue before—here's what's going on and how to fix it:
The Root Problem
Your current check data?.count != 0 doesn't work for empty API responses because most APIs don't return no data at all when there's nothing to send. Instead, they send an empty JSON structure like [] (empty array) or {} (empty object). These have a non-zero data count (e.g., [] is 2 bytes), so your check passes, but trying to process this empty JSON later (like assuming it has elements to unpack) causes the crash at JSONSerialization or beyond.
The Fix: Validate Parsed JSON, Not Data Length
Instead of relying on data size, focus on safely parsing the JSON first, then checking if the parsed result has actual content. Here's how to rewrite your getData() function properly:
func getData() { guard let url = URL(string:"http://xxxxx/xxx?xx=\(userid)") else { print("Invalid API URL") return } URLSession.shared.dataTask(with: url) { [weak self] data, response, error in // 1. Handle network errors first if let error = error { print("API request failed: \(error.localizedDescription)") return } // 2. Safely unwrap data (in case the API truly returns nothing) guard let responseData = data else { print("No data received from API") // Update UI for empty state here DispatchQueue.main.async { // e.g., self?.showEmptyStateMessage() } return } // 3. Safely parse JSON with do-catch to avoid crashes do { // Replace [[String: Any]] with your expected JSON type (e.g., [String: Any] for objects) guard let parsedData = try JSONSerialization.jsonObject(with: responseData, options: []) as? [[String: Any]] else { print("Received invalid JSON format") return } // 4. Check if the parsed data is actually empty if parsedData.isEmpty { print("API returned no results") DispatchQueue.main.async { // Handle empty state in UI (e.g., show "No items found" label) } return } // 5. Process valid, non-empty data DispatchQueue.main.async { // Update your data source and UI here // e.g., self?.items = parsedData; self?.tableView.reloadData() } } catch { print("JSON parsing error: \(error.localizedDescription)") } }.resume() }
Key Improvements
- No more data.count checks: We validate the content of the JSON, not its raw byte size.
- Safe parsing: The
do-catchblock ensuresJSONSerializationwon't crash even if the API returns malformed JSON. - Explicit type checking: Using
as? [[String: Any]]ensures we only proceed if the JSON matches the structure your app expects. - Empty state handling: We explicitly check if the parsed data is empty and handle that case gracefully instead of letting the app crash.
Bonus Tip
If your API returns a wrapped object (like {"results": []} instead of a raw empty array), adjust the parsing step to first extract the inner array:
guard let responseObject = try JSONSerialization.jsonObject(with: responseData, options: []) as? [String: Any], let parsedData = responseObject["results"] as? [[String: Any]] else { print("Invalid JSON structure") return }
内容的提问来源于stack exchange,提问作者Kavitha

