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
|
// 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
|
/// Create a new AuthViewModel instance
|
||||||
func makeAuthViewModel() -> ComposeApp.AuthViewModel {
|
func makeAuthViewModel() -> ComposeApp.AuthViewModel {
|
||||||
ComposeApp.AuthViewModel()
|
ComposeApp.AuthViewModel(dataManager: ComposeApp.DataManager.shared)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new ResidenceViewModel instance
|
/// Create a new ResidenceViewModel instance
|
||||||
func makeResidenceViewModel() -> ComposeApp.ResidenceViewModel {
|
func makeResidenceViewModel() -> ComposeApp.ResidenceViewModel {
|
||||||
ComposeApp.ResidenceViewModel()
|
ComposeApp.ResidenceViewModel(dataManager: ComposeApp.DataManager.shared)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new TaskViewModel instance
|
/// Create a new TaskViewModel instance
|
||||||
func makeTaskViewModel() -> ComposeApp.TaskViewModel {
|
func makeTaskViewModel() -> ComposeApp.TaskViewModel {
|
||||||
ComposeApp.TaskViewModel()
|
ComposeApp.TaskViewModel(dataManager: ComposeApp.DataManager.shared)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new ContractorViewModel instance
|
/// Create a new ContractorViewModel instance
|
||||||
func makeContractorViewModel() -> ComposeApp.ContractorViewModel {
|
func makeContractorViewModel() -> ComposeApp.ContractorViewModel {
|
||||||
ComposeApp.ContractorViewModel()
|
ComposeApp.ContractorViewModel(dataManager: ComposeApp.DataManager.shared)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new DocumentViewModel instance
|
/// Create a new DocumentViewModel instance
|
||||||
func makeDocumentViewModel() -> ComposeApp.DocumentViewModel {
|
func makeDocumentViewModel() -> ComposeApp.DocumentViewModel {
|
||||||
ComposeApp.DocumentViewModel()
|
ComposeApp.DocumentViewModel(dataManager: ComposeApp.DataManager.shared)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Service Factories
|
// MARK: - Service Factories
|
||||||
|
|||||||
@@ -13,6 +13,12 @@ class AppleSignInViewModel: ObservableObject {
|
|||||||
|
|
||||||
// MARK: - Private Properties
|
// MARK: - Private Properties
|
||||||
private let appleSignInManager = AppleSignInManager()
|
private let appleSignInManager = AppleSignInManager()
|
||||||
|
private let dataManager: DataManagerObservable
|
||||||
|
|
||||||
|
// MARK: - Initialization
|
||||||
|
init(dataManager: DataManagerObservable = .shared) {
|
||||||
|
self.dataManager = dataManager
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Callbacks
|
// MARK: - Callbacks
|
||||||
var onSignInSuccess: ((Bool) -> Void)? // Bool indicates if user is verified
|
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 isSubmitting = false
|
||||||
@Published private(set) var submitError: String?
|
@Published private(set) var submitError: String?
|
||||||
|
|
||||||
|
// MARK: - Private Properties
|
||||||
|
private let dataManager: DataManagerObservable
|
||||||
|
|
||||||
|
// MARK: - Initialization
|
||||||
|
init(dataManager: DataManagerObservable = .shared) {
|
||||||
|
self.dataManager = dataManager
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Loads
|
// MARK: - Loads
|
||||||
|
|
||||||
func loadSuggestions(residenceId: Int32) async {
|
func loadSuggestions(residenceId: Int32) async {
|
||||||
|
|||||||
@@ -31,8 +31,15 @@ class PasswordResetViewModel: ObservableObject {
|
|||||||
// Cancellable delayed transition task
|
// Cancellable delayed transition task
|
||||||
private var delayedTransitionTask: Task<Void, Never>?
|
private var delayedTransitionTask: Task<Void, Never>?
|
||||||
|
|
||||||
|
// MARK: - Private Properties
|
||||||
|
private let dataManager: DataManagerObservable
|
||||||
|
|
||||||
// MARK: - Initialization
|
// 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 we have a reset token from deep link, skip to password reset step
|
||||||
if let token = resetToken {
|
if let token = resetToken {
|
||||||
self.resetToken = token
|
self.resetToken = token
|
||||||
|
|||||||
@@ -15,8 +15,13 @@ class RegisterViewModel: ObservableObject {
|
|||||||
@Published var errorMessage: String?
|
@Published var errorMessage: String?
|
||||||
@Published var isRegistered: Bool = false
|
@Published var isRegistered: Bool = false
|
||||||
|
|
||||||
|
// MARK: - Private Properties
|
||||||
|
private let dataManager: DataManagerObservable
|
||||||
|
|
||||||
// MARK: - Initialization
|
// MARK: - Initialization
|
||||||
init() {}
|
init(dataManager: DataManagerObservable = .shared) {
|
||||||
|
self.dataManager = dataManager
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Public Methods
|
// MARK: - Public Methods
|
||||||
func register() {
|
func register() {
|
||||||
|
|||||||
@@ -14,10 +14,15 @@ class VerifyEmailViewModel: ObservableObject {
|
|||||||
|
|
||||||
// MARK: - Private Properties
|
// MARK: - Private Properties
|
||||||
private let tokenStorage: TokenStorageProtocol
|
private let tokenStorage: TokenStorageProtocol
|
||||||
|
private let dataManager: DataManagerObservable
|
||||||
|
|
||||||
// MARK: - Initialization
|
// MARK: - Initialization
|
||||||
init(tokenStorage: TokenStorageProtocol? = nil) {
|
init(
|
||||||
|
tokenStorage: TokenStorageProtocol? = nil,
|
||||||
|
dataManager: DataManagerObservable = .shared
|
||||||
|
) {
|
||||||
self.tokenStorage = tokenStorage ?? Dependencies.current.makeTokenStorage()
|
self.tokenStorage = tokenStorage ?? Dependencies.current.makeTokenStorage()
|
||||||
|
self.dataManager = dataManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Public Methods
|
// MARK: - Public Methods
|
||||||
|
|||||||
@@ -66,6 +66,10 @@ struct iOSApp: App {
|
|||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
RootView(deepLinkResetToken: $deepLinkResetToken)
|
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(themeManager)
|
||||||
.environmentObject(contractorSharingManager)
|
.environmentObject(contractorSharingManager)
|
||||||
.environmentObject(residenceSharingManager)
|
.environmentObject(residenceSharingManager)
|
||||||
|
|||||||
Reference in New Issue
Block a user