From 42ccbdcbd6d392e9a7ea740a56dca6ab978fcad4 Mon Sep 17 00:00:00 2001 From: Trey T Date: Sun, 19 Apr 2026 18:47:58 -0500 Subject: [PATCH] =?UTF-8?q?P2:=20iOS=20Full=20DI=20=E2=80=94=20all=2011=20?= =?UTF-8?q?VMs=20accept=20dataManager=20init=20param?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the DI seam to the 5 previously singleton-coupled VMs: - VerifyEmailViewModel - RegisterViewModel - PasswordResetViewModel - AppleSignInViewModel - OnboardingTasksViewModel All now accept init(dataManager: DataManagerObservable = .shared). iOSApp.swift injects DataManagerObservable.shared at the root via .environmentObject so descendant views can reach it via @EnvironmentObject without implicit singleton reads. Dependencies.swift factories updated to pass DataManager.shared explicitly into Kotlin VM constructors — SKIE doesn't surface Kotlin default init parameters as Swift defaults, so every Kotlin VM call-site needs the explicit argument. Affects makeAuthViewModel, makeResidenceViewModel, makeTaskViewModel, makeContractorViewModel, makeDocumentViewModel. Full iOS build green. Co-Authored-By: Claude Opus 4.7 (1M context) --- iosApp/iosApp/Core/Dependencies.swift | 13 ++++++++----- iosApp/iosApp/Login/AppleSignInViewModel.swift | 6 ++++++ .../Onboarding/OnboardingTasksViewModel.swift | 8 ++++++++ .../PasswordReset/PasswordResetViewModel.swift | 9 ++++++++- iosApp/iosApp/Register/RegisterViewModel.swift | 7 ++++++- .../iosApp/VerifyEmail/VerifyEmailViewModel.swift | 7 ++++++- iosApp/iosApp/iOSApp.swift | 4 ++++ 7 files changed, 46 insertions(+), 8 deletions(-) diff --git a/iosApp/iosApp/Core/Dependencies.swift b/iosApp/iosApp/Core/Dependencies.swift index 36c0b9c..048de4d 100644 --- a/iosApp/iosApp/Core/Dependencies.swift +++ b/iosApp/iosApp/Core/Dependencies.swift @@ -40,29 +40,32 @@ final class Dependencies { // MARK: - Kotlin ViewModel Factories + // SKIE doesn't expose Kotlin default constructor params to Swift, so + // pass DataManager.shared explicitly on each factory. + /// Create a new AuthViewModel instance func makeAuthViewModel() -> ComposeApp.AuthViewModel { - ComposeApp.AuthViewModel() + ComposeApp.AuthViewModel(dataManager: ComposeApp.DataManager.shared) } /// Create a new ResidenceViewModel instance func makeResidenceViewModel() -> ComposeApp.ResidenceViewModel { - ComposeApp.ResidenceViewModel() + ComposeApp.ResidenceViewModel(dataManager: ComposeApp.DataManager.shared) } /// Create a new TaskViewModel instance func makeTaskViewModel() -> ComposeApp.TaskViewModel { - ComposeApp.TaskViewModel() + ComposeApp.TaskViewModel(dataManager: ComposeApp.DataManager.shared) } /// Create a new ContractorViewModel instance func makeContractorViewModel() -> ComposeApp.ContractorViewModel { - ComposeApp.ContractorViewModel() + ComposeApp.ContractorViewModel(dataManager: ComposeApp.DataManager.shared) } /// Create a new DocumentViewModel instance func makeDocumentViewModel() -> ComposeApp.DocumentViewModel { - ComposeApp.DocumentViewModel() + ComposeApp.DocumentViewModel(dataManager: ComposeApp.DataManager.shared) } // MARK: - Service Factories diff --git a/iosApp/iosApp/Login/AppleSignInViewModel.swift b/iosApp/iosApp/Login/AppleSignInViewModel.swift index ca4a193..edfddfe 100644 --- a/iosApp/iosApp/Login/AppleSignInViewModel.swift +++ b/iosApp/iosApp/Login/AppleSignInViewModel.swift @@ -13,6 +13,12 @@ class AppleSignInViewModel: ObservableObject { // MARK: - Private Properties private let appleSignInManager = AppleSignInManager() + private let dataManager: DataManagerObservable + + // MARK: - Initialization + init(dataManager: DataManagerObservable = .shared) { + self.dataManager = dataManager + } // MARK: - Callbacks var onSignInSuccess: ((Bool) -> Void)? // Bool indicates if user is verified diff --git a/iosApp/iosApp/Onboarding/OnboardingTasksViewModel.swift b/iosApp/iosApp/Onboarding/OnboardingTasksViewModel.swift index 56257f6..835a4f8 100644 --- a/iosApp/iosApp/Onboarding/OnboardingTasksViewModel.swift +++ b/iosApp/iosApp/Onboarding/OnboardingTasksViewModel.swift @@ -33,6 +33,14 @@ final class OnboardingTasksViewModel: ObservableObject { @Published private(set) var isSubmitting = false @Published private(set) var submitError: String? + // MARK: - Private Properties + private let dataManager: DataManagerObservable + + // MARK: - Initialization + init(dataManager: DataManagerObservable = .shared) { + self.dataManager = dataManager + } + // MARK: - Loads func loadSuggestions(residenceId: Int32) async { diff --git a/iosApp/iosApp/PasswordReset/PasswordResetViewModel.swift b/iosApp/iosApp/PasswordReset/PasswordResetViewModel.swift index 37f170a..ae0e57f 100644 --- a/iosApp/iosApp/PasswordReset/PasswordResetViewModel.swift +++ b/iosApp/iosApp/PasswordReset/PasswordResetViewModel.swift @@ -31,8 +31,15 @@ class PasswordResetViewModel: ObservableObject { // Cancellable delayed transition task private var delayedTransitionTask: Task? + // MARK: - Private Properties + private let dataManager: DataManagerObservable + // MARK: - Initialization - init(resetToken: String? = nil) { + init( + resetToken: String? = nil, + dataManager: DataManagerObservable = .shared + ) { + self.dataManager = dataManager // If we have a reset token from deep link, skip to password reset step if let token = resetToken { self.resetToken = token diff --git a/iosApp/iosApp/Register/RegisterViewModel.swift b/iosApp/iosApp/Register/RegisterViewModel.swift index 4ab799a..1269c99 100644 --- a/iosApp/iosApp/Register/RegisterViewModel.swift +++ b/iosApp/iosApp/Register/RegisterViewModel.swift @@ -15,8 +15,13 @@ class RegisterViewModel: ObservableObject { @Published var errorMessage: String? @Published var isRegistered: Bool = false + // MARK: - Private Properties + private let dataManager: DataManagerObservable + // MARK: - Initialization - init() {} + init(dataManager: DataManagerObservable = .shared) { + self.dataManager = dataManager + } // MARK: - Public Methods func register() { diff --git a/iosApp/iosApp/VerifyEmail/VerifyEmailViewModel.swift b/iosApp/iosApp/VerifyEmail/VerifyEmailViewModel.swift index 59b474b..78ad1ab 100644 --- a/iosApp/iosApp/VerifyEmail/VerifyEmailViewModel.swift +++ b/iosApp/iosApp/VerifyEmail/VerifyEmailViewModel.swift @@ -14,10 +14,15 @@ class VerifyEmailViewModel: ObservableObject { // MARK: - Private Properties private let tokenStorage: TokenStorageProtocol + private let dataManager: DataManagerObservable // MARK: - Initialization - init(tokenStorage: TokenStorageProtocol? = nil) { + init( + tokenStorage: TokenStorageProtocol? = nil, + dataManager: DataManagerObservable = .shared + ) { self.tokenStorage = tokenStorage ?? Dependencies.current.makeTokenStorage() + self.dataManager = dataManager } // MARK: - Public Methods diff --git a/iosApp/iosApp/iOSApp.swift b/iosApp/iosApp/iOSApp.swift index 190e21d..48b84ef 100644 --- a/iosApp/iosApp/iOSApp.swift +++ b/iosApp/iosApp/iOSApp.swift @@ -66,6 +66,10 @@ struct iOSApp: App { var body: some Scene { WindowGroup { RootView(deepLinkResetToken: $deepLinkResetToken) + // Single source of truth injection — every descendant view can + // reach @EnvironmentObject var dataManager: DataManagerObservable + // without reading the .shared singleton implicitly. + .environmentObject(DataManagerObservable.shared) .environmentObject(themeManager) .environmentObject(contractorSharingManager) .environmentObject(residenceSharingManager)