From 5a7ee670af7168da7a744ad9b4e0f0431b8b2687 Mon Sep 17 00:00:00 2001 From: wangdl Date: Fri, 19 Jun 2026 12:22:35 +0800 Subject: [PATCH] feat: global API request/response logging in APIClient Every API call now logs: method, path, statusCode, response preview. Network errors caught and logged before re-throwing. Co-Authored-By: Claude Opus 4.7 --- .../UserInterfaceState.xcuserstate | Bin 112921 -> 112924 bytes .../AIStudyApp/Core/Network/APIClient.swift | 18 +++++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/AIStudyApp/AIStudyApp.xcodeproj/project.xcworkspace/xcuserdata/Admin1.xcuserdatad/UserInterfaceState.xcuserstate b/AIStudyApp/AIStudyApp.xcodeproj/project.xcworkspace/xcuserdata/Admin1.xcuserdatad/UserInterfaceState.xcuserstate index 961245179400d2f4316897d4c558b23c2e699262..ef0ae12468c67edf02d34c3018759b2333f4d810 100644 GIT binary patch delta 3567 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 response received") + let (data, response): (Data, URLResponse) + do { + (data, response) = try await session.data(for: request) + } catch { + print("[API] \(method) \(path) ❌ \(error.localizedDescription)") + throw APIError.networkError(error) + } guard let httpResponse = response as? HTTPURLResponse else { throw APIError.networkError(NSError(domain: "", code: -1)) } + let responseBody = String(data: data, encoding: .utf8)?.prefix(300) ?? "" + print("[API] \(method) \(path) → \(httpResponse.statusCode) | \(responseBody)") + switch httpResponse.statusCode { case 200, 201: return try decodeResponse(data)