fix(ios): APIClient 兼容有无 ResponseInterceptor 两种响应格式

服务器部署版本可能未启用 ResponseInterceptor,返回裸 JSON 而非
{ success, data, timestamp } 信封格式。APIClient 解码时先探测
JSON 结构,自动适配包裹/裸数据两种格式。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
WangDL 2026-05-17 19:42:17 +08:00
parent 5e19bd740e
commit b182203464

View File

@ -63,12 +63,7 @@ actor APIClient {
switch httpResponse.statusCode { switch httpResponse.statusCode {
case 200, 201: case 200, 201:
do { return try decodeResponse(data)
let envelope = try JSONDecoder().decode(APIEnvelope<T>.self, from: data)
return envelope.data
} catch {
throw APIError.decodingFailed(error.localizedDescription)
}
case 401 where !isRetry: case 401 where !isRetry:
if let newToken = await refreshAccessToken() { if let newToken = await refreshAccessToken() {
self.token = newToken self.token = newToken
@ -109,6 +104,25 @@ actor APIClient {
NotificationCenter.default.post(name: .tokenExpired, object: nil) NotificationCenter.default.post(name: .tokenExpired, object: nil)
} }
} }
// MARK: - Decoding
private func decodeResponse<T: Decodable>(_ data: Data) throws -> T {
let decoder = JSONDecoder()
// Try unwrapped response first (no envelope), then wrapped
if let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
json["data"] == nil && json["success"] == nil {
return try decoder.decode(T.self, from: data)
}
// Has envelope wrapper
do {
let envelope = try decoder.decode(APIEnvelope<T>.self, from: data)
return envelope.data
} catch {
// Fallback: try decoding T directly (e.g. server returns unwrapped on some endpoints)
return try decoder.decode(T.self, from: data)
}
}
} }
// MARK: - Helper for encoding arbitrary Encodable // MARK: - Helper for encoding arbitrary Encodable