Valerio
Valerio

Reputation: 3615

Swift ObservableObject implementation with generic inference

I must admit my knowledge of swift is limited, and I cannot wrap my head around this problem.

I've defined this protocol, so I can use different auth providers in my app.

protocol AuthRepository {
    associatedtype AuthData
    associatedtype AuthResponseData
    associatedtype RegistrationData
    associatedtype RegistrationResponseData
    
    func login(with data: AuthData) async throws -> AuthResponseData?
    
    func register(with data: RegistrationData) async throws -> RegistrationResponseData?
}

and an implementation for my server

struct MyServerAuthData {
    let email: String
    let password: String
}

struct  MyServerAuthResponseData {
    let token: String
}

struct  MyServerRegistrationData {
    let email: String
    let password: String
    let name: String
}

actor AuthRepositoryImpl: AuthRepository {
    func login(with data: MyServerAuthData) async throws ->  MyServerAuthResponseData? {
        ...
    }
    
    func register(with data:  MyServerRegistrationData) async throws -> Void? {
        ...
    }
}

To use across the app, I've created this ViewModel

@MainActor
final class AuthViewModel<T: AuthRepository>: ObservableObject {
    private let repository: T

    init(repository: T) {
        self.repository = repository
    }

    func login(data: T.AuthData) async throws -> T.AuthResponseData? {
        try await repository.login(with: data)
    }

    func register(with data: T.RegistrationData) async throws {
        try await repository.register(with: data)
    }
}

defined in the app as

@main
struct MyApp: App {
    @StateObject var authViewModel = AuthViewModel(repository: AuthRepositoryImpl())

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(self.authViewModel)
        }
    }
}

and consumed as

@EnvironmentObject private var authViewModel: AuthViewModel<AuthRepositoryImpl>

But with this code, the whole concept of having a generic implementation for the auth repository is useless, because changing the AuthRepostory will need to search and replace AuthViewModel<AuthRepositoryImpl> across all the app.

I've experienced this directly creating a MockAuthImpl to use with #Preview, and the preview crashed because it defines AuthViewModel(repository: MockAuthImpl()) but the view expects AuthViewModel.

There is a better way to do that?

Upvotes: -1

Views: 93

Answers (0)

Related Questions