Files
pos-system/microservices/apps/app-client-base-swift/AppClientBaseSwift/APIUsageExamples.swift
Ho Ngoc Hai 76d75c753b Migrate
2026-05-23 18:37:02 +07:00

405 lines
9.2 KiB
Swift

//
// APIUsageExamples.swift
// AppClientBaseSwift
//
// Examples of using APIResponse wrapper
// Ví d s dng APIResponse wrapper
//
import Foundation
/*
HƯNG DN S DNG API RESPONSE WRAPPER
======================================
Server ca bn tr v response vi format:
{
"success": true,
"data": { ... } hoc [ ... ],
"error": null,
"pagination": null
}
Tt c API calls phi s dng 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<User> = try await APIService.shared.get(
endpoint: "/users/me"
)
let user = try response.unwrap()
return user
// Cách 2: Manual check
// let response: APIResponse<User> = 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<User> = try await APIService.shared.get(
endpoint: "/users?page=1"
)
// Unwrap đ ly 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<Post> = 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 cn 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<User> = try await APIService.shared.put(
endpoint: "/users/profile",
body: request
)
return try response.unwrap()
}
// MARK: - Example 6: Error Handling
// Ví d 6: X lý li
func exampleWithErrorHandling() async {
do {
let response: APIResponse<User> = 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 lng 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<Comment> = 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 vi 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<Post> = 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 cn)
/*
POST /api/v1/users/avatar
Content-Type: multipart/form-data
Note: Cn implement multipart form data nếu cn upload file
Hin ti APIService ch h tr JSON
*/
// MARK: - Example 10: Custom Headers
// Ví d 10: Custom headers
func exampleWithCustomHeaders() async throws -> User {
// Nếu cn thêm headers đc bit
let response: APIResponse<User> = 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<T> hoc ListResponse<T>
2. Dùng unwrap() helper đ code ngn gn
3. Handle errors properly vi do-catch
4. Log errors trong development
5. Validate request data trưc khi gi API
6. Cache data khi cn (như User data)
DON'T - Không nên làm:
1. Không decode trc 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 vi synchronous calls
*/
// MARK: - Testing Tips
// Tips test API
/*
Test APIs trong development:
1. Dùng APITestView đ test tng endpoint
2. Check Console logs vi DebugLogger
3. Verify JSON response structure
4. Test error cases (401, 404, 500, etc.)
5. Test vi slow network / offline
Mock data cho UI testing:
#if DEBUG
func mockFetchUser() async -> User {
return User.sample
}
#endif
*/