// // APIUsageExamples.swift // AppClientBaseSwift // // Examples of using APIResponse wrapper // Ví dụ sử dụng APIResponse wrapper // import Foundation /* HƯỚNG DẪN SỬ DỤNG API RESPONSE WRAPPER ====================================== Server của bạn trả về response với format: { "success": true, "data": { ... } hoặc [ ... ], "error": null, "pagination": null } Tất cả API calls phải sử dụng wrapper tương ứng. */ // MARK: - Example 1: Single Object Response // Ví dụ 1: Response trả về 1 object /* GET /api/v1/users/me Response: { "success": true, "data": { "id": "...", "email": "...", "name": "..." }, "error": null, "pagination": null } */ func exampleFetchUser() async throws -> User { // Cách 1: Dùng unwrap() helper (recommended) let response: APIResponse = try await APIService.shared.get( endpoint: "/users/me" ) let user = try response.unwrap() return user // Cách 2: Manual check // let response: APIResponse = try await APIService.shared.get(endpoint: "/users/me") // guard response.success, let user = response.data else { // throw APIError.serverError(statusCode: 400, message: response.error ?? "Failed") // } // return user } // MARK: - Example 2: List/Array Response // Ví dụ 2: Response trả về danh sách /* GET /api/v1/users?page=1 Response: { "success": true, "data": [ { "id": "1", "email": "...", "name": "..." }, { "id": "2", "email": "...", "name": "..." } ], "error": null, "pagination": { "currentPage": 1, "pageSize": 20, "totalPages": 5, "totalItems": 100 } } */ func exampleFetchUsers() async throws -> [User] { // Dùng ListResponse wrapper let response: ListResponse = try await APIService.shared.get( endpoint: "/users?page=1" ) // Unwrap để lấy array let users = try response.unwrap() // Có thể access pagination info if let pagination = response.pagination { print("Page \(pagination.currentPage ?? 0) of \(pagination.totalPages ?? 0)") print("Total items: \(pagination.totalItems ?? 0)") } return users } // MARK: - Example 3: POST Request with Response Data // Ví dụ 3: POST request có response data /* POST /api/v1/posts Body: { "title": "...", "content": "..." } Response: { "success": true, "data": { "id": "post_123", "title": "...", "content": "...", "createdAt": "..." }, "error": null, "pagination": null } */ struct Post: Codable { let id: String let title: String let content: String let createdAt: Date? } struct CreatePostRequest: Encodable { let title: String let content: String } func exampleCreatePost(title: String, content: String) async throws -> Post { let request = CreatePostRequest(title: title, content: content) let response: APIResponse = try await APIService.shared.post( endpoint: "/posts", body: request ) return try response.unwrap() } // MARK: - Example 4: DELETE/UPDATE Requests // Ví dụ 4: DELETE/UPDATE requests /* DELETE /api/v1/posts/123 Response: { "success": true, "data": null, "error": null, "pagination": null } */ func exampleDeletePost(id: String) async throws { // Dùng SimpleResponse khi không cần data let response: SimpleResponse = try await APIService.shared.delete( endpoint: "/posts/\(id)" ) if !response.success { throw APIError.serverError( statusCode: 400, message: response.error ?? response.message ?? "Delete failed" ) } } // MARK: - Example 5: PUT/PATCH Request // Ví dụ 5: PUT/PATCH request /* PUT /api/v1/users/profile Body: { "name": "New Name", "phoneNumber": "..." } Response: { "success": true, "data": { "id": "...", "email": "...", "name": "New Name", ... }, "error": null, "pagination": null } */ struct UpdateProfileRequest: Encodable { let name: String? let phoneNumber: String? } func exampleUpdateProfile(name: String?, phoneNumber: String?) async throws -> User { let request = UpdateProfileRequest(name: name, phoneNumber: phoneNumber) let response: APIResponse = try await APIService.shared.put( endpoint: "/users/profile", body: request ) return try response.unwrap() } // MARK: - Example 6: Error Handling // Ví dụ 6: Xử lý lỗi func exampleWithErrorHandling() async { do { let response: APIResponse = try await APIService.shared.get( endpoint: "/users/me" ) let user = try response.unwrap() print("Success: \(user.email)") } catch let error as APIError { switch error { case .decodingError(let decodingError): print("❌ Decoding error: \(decodingError)") // Server response structure doesn't match expected model case .unauthorized: print("❌ Unauthorized - token invalid or expired") // Need to login again case .networkError(let networkError): print("❌ Network error: \(networkError)") // No internet connection or server unreachable case .serverError(let statusCode, let message): print("❌ Server error \(statusCode): \(message ?? "Unknown")") // Server returned error response case .noData: print("❌ No data received") default: print("❌ Unknown error: \(error)") } } catch { print("❌ Unexpected error: \(error)") } } // MARK: - Example 7: Nested Objects // Ví dụ 7: Objects lồng nhau /* GET /api/v1/posts/123/comments Response: { "success": true, "data": [ { "id": "comment_1", "content": "Great post!", "author": { "id": "user_1", "name": "John Doe", "avatarUrl": "..." }, "createdAt": "..." } ], "error": null, "pagination": { ... } } */ struct Comment: Codable { let id: String let content: String let author: CommentAuthor let createdAt: Date? } struct CommentAuthor: Codable { let id: String let name: String let avatarUrl: String? } func exampleFetchComments(postId: String) async throws -> [Comment] { let response: ListResponse = try await APIService.shared.get( endpoint: "/posts/\(postId)/comments" ) return try response.unwrap() } // MARK: - Example 8: Search/Filter with Query Parameters // Ví dụ 8: Search/Filter với query parameters /* GET /api/v1/posts?search=swift&category=tutorial&page=1&pageSize=20 */ func exampleSearchPosts( search: String?, category: String?, page: Int = 1, pageSize: Int = 20 ) async throws -> [Post] { var queryItems: [String] = [] if let search = search { queryItems.append("search=\(search)") } if let category = category { queryItems.append("category=\(category)") } queryItems.append("page=\(page)") queryItems.append("pageSize=\(pageSize)") let queryString = queryItems.joined(separator: "&") let endpoint = "/posts?\(queryString)" let response: ListResponse = try await APIService.shared.get( endpoint: endpoint ) return try response.unwrap() } // MARK: - Example 9: Upload File (if needed) // Ví dụ 9: Upload file (nếu cần) /* POST /api/v1/users/avatar Content-Type: multipart/form-data Note: Cần implement multipart form data nếu cần upload file Hiện tại APIService chỉ hỗ trợ JSON */ // MARK: - Example 10: Custom Headers // Ví dụ 10: Custom headers func exampleWithCustomHeaders() async throws -> User { // Nếu cần thêm headers đặc biệt let response: APIResponse = try await APIService.shared.request( endpoint: "/users/me", method: .get, body: nil as String?, headers: [ "X-Custom-Header": "custom-value", "X-Request-ID": UUID().uuidString ] ) return try response.unwrap() } // MARK: - Best Practices // Các best practices /* ✅ DO - Nên làm: 1. Luôn dùng APIResponse hoặc ListResponse 2. Dùng unwrap() helper để code ngắn gọn 3. Handle errors properly với do-catch 4. Log errors trong development 5. Validate request data trước khi gọi API 6. Cache data khi cần (như User data) ❌ DON'T - Không nên làm: 1. Không decode trực tiếp thành Model mà không wrapper 2. Không ignore errors 3. Không hardcode URLs, dùng Constants 4. Không lưu sensitive data vào UserDefaults (dùng Keychain) 5. Không block main thread với synchronous calls */ // MARK: - Testing Tips // Tips test API /* Test APIs trong development: 1. Dùng APITestView để test từng endpoint 2. Check Console logs với DebugLogger 3. Verify JSON response structure 4. Test error cases (401, 404, 500, etc.) 5. Test với slow network / offline Mock data cho UI testing: #if DEBUG func mockFetchUser() async -> User { return User.sample } #endif */