Swift 網路層生存指南 (3) —— API 亂象下的終極防線
發佈於 2026-02-06
這篇文章,我想聊聊一個「笑著笑著就哭了」的開發日常。
在過去開發的經驗中,常遇到 Backends 他們自己也沒 Spec, 給出的 Response 像是一場隨機發生的驚喜:
(成功 200):
- 直球對決型:
{ "id": 1, "name": "Gemini" }(直接就是 DTO)。 - 標準殼型:
{ "data": { "id": 1, "name": "Gemini" } }。 - 腦袋抽風多一層型:
{ "data": { "somekey": { "id": 1, "name": "Gemini" } } }(不知道為何要多個 key)。 - 大禮包型:
{ "data": { "list": [...] } }。
- 直球對決型:
(失敗 4xx/5xx):
- 資料欄位瞬間蒸發,只剩
{ "message": "something wrong" }。
- 資料欄位瞬間蒸發,只剩
面對這種「薛丁格的 JSON」,如果你只寫標準的 Codable,你的 Console 大概會被 keyNotFound 洗版到你懷疑人生。
🏛️ 架構圖:當 HTTP 狀態碼與動態路徑聯動
這套設計的核心在於:不再盲目相信 JSON 內容,而是透過注入「解析路徑 (decodePath)」來對付那些抽風的 Key。
graph TD
A[API Response] --> B{取得 HTTP StatusCode}
B -->|注入| C[JSONDecoder.userInfo]
C -->|定義導航| D[decodePath: data -> somekey]
subgraph BaseResponse_Init [BaseResponse 內部邏輯]
D --> E{檢查 StatusCode 是否為 200}
E -->|False| F[優先解析 message -> 拋出 APIError]
E -->|True| G{進入 ShieldedResponse 救災模式}
G --> H{依據 decodePath 深度導航}
H --> I[挖出真正的 Payload]
end
F --> J[攔截器捕捉 Error]
I --> K[交付 Domain Layer]
style BaseResponse_Init fill:#fff4dd,stroke:#d4a017,stroke-width:2px
🛠️ 核心實作:應對「抽風 Key」的 StandardResponse
透過 userInfo 注入 statusCode 與 decodePath,讓你的 Model 具備「透視眼」,直接無視那些無意義的外殼。
| |
實作範例
| |
🚩 實戰:如何優雅地解決「多一層」?
針對這幾種腦袋抽風的情境,你在呼叫端的程式碼會長這樣:
| |
為什麼這招很有效?
- 不改 DTO:你的
MyDTO永遠只需要關注欄位本身,不需要為了後端多包一個SomeKeyContainer。 - 動態適應:同一個 DTO,在不同的 Endpoint 可能被包在不同的 Key 裡面。你只需要在 Request 層微調
decodePath即可。
💡 總結:給同樣在「掃雷」的你
這套架構的核心哲學是:「把變動留在外面,把穩定留給 Model」。
後端的心情我們無法預測,但我們可以決定我們的解析引擎有多強大。面對那種「data 裡面還要包一層 somekey」的設計,我們不抱怨,我們直接用導航鑽過去。
身為 iOS 工程師,我們的尊嚴不是寫出多炫的動畫,而是**「無論 API 怎麼抽風,我的數據轉換依然優雅、精準、不崩潰」**。
本文由 Gemini 3 Flash (AI) 協助撰寫 我見過無數個因為 JSON 多了一層 Key 而崩潰的 App。這套「路徑鑽頭」架構是你的最佳護身符——願你的 DTO 永遠乾淨。