Implement unified network layer with APILayer and migrate iOS ViewModels
Major architectural improvements: - Created APILayer as single entry point for all network operations - Integrated cache-first reads with automatic cache updates on mutations - Migrated all shared Kotlin ViewModels to use APILayer instead of direct API calls - Migrated iOS ViewModels to wrap shared Kotlin ViewModels with StateFlow observation - Replaced LookupsManager with DataCache for centralized lookup data management - Added password reset methods to AuthViewModel - Added task completion and update methods to APILayer - Added residence user management methods to APILayer iOS specific changes: - Updated LoginViewModel, RegisterViewModel, ProfileViewModel to use shared AuthViewModel - Updated ContractorViewModel, DocumentViewModel to use shared ViewModels - Updated ResidenceViewModel to use shared ViewModel and APILayer - Updated TaskViewModel to wrap shared ViewModel with callback-based interface - Migrated PasswordResetViewModel and VerifyEmailViewModel to shared AuthViewModel - Migrated AllTasksView, CompleteTaskView, EditTaskView to use APILayer - Migrated ManageUsersView, ResidenceDetailView to use APILayer - Migrated JoinResidenceView to use async/await pattern with APILayer - Removed LookupsManager.swift in favor of DataCache - Fixed PushNotificationManager @MainActor issue - Converted all direct API calls to use async/await with proper error handling Benefits: - Reduced code duplication between iOS and Android - Consistent error handling across platforms - Automatic cache management for better performance - Centralized network layer for easier testing and maintenance - Net reduction of ~700 lines of code through shared logic 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -282,15 +282,14 @@ struct CompleteTaskView: View {
|
||||
}
|
||||
|
||||
private func handleComplete() {
|
||||
isSubmitting = true
|
||||
|
||||
guard let token = TokenStorage.shared.getToken() else {
|
||||
guard TokenStorage.shared.getToken() != nil else {
|
||||
errorMessage = "Not authenticated"
|
||||
showError = true
|
||||
isSubmitting = false
|
||||
return
|
||||
}
|
||||
|
||||
isSubmitting = true
|
||||
|
||||
// Get current date in ISO format
|
||||
let dateFormatter = ISO8601DateFormatter()
|
||||
let currentDate = dateFormatter.string(from: Date())
|
||||
@@ -310,48 +309,52 @@ struct CompleteTaskView: View {
|
||||
rating: KotlinInt(int: Int32(rating))
|
||||
)
|
||||
|
||||
let completionApi = TaskCompletionApi(client: ApiClient_iosKt.createHttpClient())
|
||||
Task {
|
||||
do {
|
||||
let result: ApiResult<TaskCompletion>
|
||||
|
||||
// If there are images, upload with images
|
||||
if !selectedImages.isEmpty {
|
||||
// Compress images to meet size requirements
|
||||
let imageDataArray = ImageCompression.compressImages(selectedImages)
|
||||
let imageByteArrays = imageDataArray.map { KotlinByteArray(data: $0) }
|
||||
let fileNames = (0..<imageDataArray.count).map { "image_\($0).jpg" }
|
||||
// If there are images, upload with images
|
||||
if !selectedImages.isEmpty {
|
||||
// Compress images to meet size requirements
|
||||
let imageDataArray = ImageCompression.compressImages(selectedImages)
|
||||
let imageByteArrays = imageDataArray.map { KotlinByteArray(data: $0) }
|
||||
let fileNames = (0..<imageDataArray.count).map { "image_\($0).jpg" }
|
||||
|
||||
completionApi.createCompletionWithImages(
|
||||
token: token,
|
||||
request: request,
|
||||
images: imageByteArrays,
|
||||
imageFileNames: fileNames
|
||||
) { result, error in
|
||||
handleCompletionResult(result: result, error: error)
|
||||
}
|
||||
} else {
|
||||
// Upload without images
|
||||
completionApi.createCompletion(token: token, request: request) { result, error in
|
||||
handleCompletionResult(result: result, error: error)
|
||||
result = try await APILayer.shared.createTaskCompletionWithImages(
|
||||
request: request,
|
||||
images: imageByteArrays,
|
||||
imageFileNames: fileNames
|
||||
)
|
||||
} else {
|
||||
// Upload without images
|
||||
result = try await APILayer.shared.createTaskCompletion(request: request)
|
||||
}
|
||||
|
||||
await MainActor.run {
|
||||
if result is ApiResultSuccess<TaskCompletion> {
|
||||
self.isSubmitting = false
|
||||
self.dismiss()
|
||||
self.onComplete()
|
||||
} else if let errorResult = result as? ApiResultError {
|
||||
self.errorMessage = errorResult.message
|
||||
self.showError = true
|
||||
self.isSubmitting = false
|
||||
} else {
|
||||
self.errorMessage = "Failed to complete task"
|
||||
self.showError = true
|
||||
self.isSubmitting = false
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
self.errorMessage = error.localizedDescription
|
||||
self.showError = true
|
||||
self.isSubmitting = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func handleCompletionResult(result: ApiResult<TaskCompletion>?, error: Error?) {
|
||||
DispatchQueue.main.async {
|
||||
if result is ApiResultSuccess<TaskCompletion> {
|
||||
self.isSubmitting = false
|
||||
self.dismiss()
|
||||
self.onComplete()
|
||||
} else if let errorResult = result as? ApiResultError {
|
||||
self.errorMessage = errorResult.message
|
||||
self.showError = true
|
||||
self.isSubmitting = false
|
||||
} else if let error = error {
|
||||
self.errorMessage = error.localizedDescription
|
||||
self.showError = true
|
||||
self.isSubmitting = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper extension to convert Data to KotlinByteArray
|
||||
|
||||
Reference in New Issue
Block a user