P2: iOS Full DI — all 11 VMs accept dataManager init param
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) <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -31,8 +31,15 @@ class PasswordResetViewModel: ObservableObject {
|
||||
// Cancellable delayed transition task
|
||||
private var delayedTransitionTask: Task<Void, Never>?
|
||||
|
||||
// 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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user