diff --git a/apple/OmnivoreKit/Sources/App/Views/Registration/CreateProfileView.swift b/apple/OmnivoreKit/Sources/App/Views/Registration/CreateProfileView.swift index e218f96b2..db73ce795 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Registration/CreateProfileView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Registration/CreateProfileView.swift @@ -5,7 +5,7 @@ import SwiftUI import Utils import Views -final class CreateProfileViewModel: ObservableObject { +@MainActor final class CreateProfileViewModel: ObservableObject { private(set) var initialUserProfile = UserProfile(username: "", name: "", bio: nil) var isConfigured = false @@ -39,7 +39,9 @@ final class CreateProfileViewModel: ObservableObject { switch profileOrError { case let .left(userProfile): - submitProfile(userProfile: userProfile, authenticator: authenticator) + Task { + await submitProfile(userProfile: userProfile, authenticator: authenticator) + } case let .right(errorMessage): validationErrorMessage = errorMessage } @@ -76,16 +78,14 @@ final class CreateProfileViewModel: ObservableObject { .store(in: &subscriptions) } - func submitProfile(userProfile: UserProfile, authenticator: Authenticator) { - authenticator - .createAccount(userProfile: userProfile).sink( - receiveCompletion: { [weak self] completion in - guard case let .failure(loginError) = completion else { return } - self?.loginError = loginError - }, - receiveValue: { _ in } - ) - .store(in: &subscriptions) + func submitProfile(userProfile: UserProfile, authenticator: Authenticator) async { + do { + try await authenticator.createAccount(userProfile: userProfile) + } catch { + if let error = error as? LoginError { + loginError = error + } + } } func configure(profile: UserProfile, dataService: DataService) { diff --git a/apple/OmnivoreKit/Sources/App/Views/Registration/NewAppleSignupView.swift b/apple/OmnivoreKit/Sources/App/Views/Registration/NewAppleSignupView.swift index d24298116..15f333b90 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Registration/NewAppleSignupView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Registration/NewAppleSignupView.swift @@ -5,23 +5,21 @@ import SwiftUI import Utils import Views -final class NewAppleSignupViewModel: ObservableObject { +@MainActor final class NewAppleSignupViewModel: ObservableObject { @Published var loginError: LoginError? var subscriptions = Set() init() {} - func submitProfile(userProfile: UserProfile, authenticator: Authenticator) { - authenticator - .createAccount(userProfile: userProfile).sink( - receiveCompletion: { [weak self] completion in - guard case let .failure(loginError) = completion else { return } - self?.loginError = loginError - }, - receiveValue: { _ in } - ) - .store(in: &subscriptions) + func submitProfile(userProfile: UserProfile, authenticator: Authenticator) async { + do { + try await authenticator.createAccount(userProfile: userProfile) + } catch { + if let error = error as? LoginError { + loginError = error + } + } } } @@ -48,7 +46,11 @@ struct NewAppleSignupView: View { VStack { Button( - action: { viewModel.submitProfile(userProfile: userProfile, authenticator: authenticator) }, + action: { + Task { + await viewModel.submitProfile(userProfile: userProfile, authenticator: authenticator) + } + }, label: { Text("Continue") } ) .buttonStyle(SolidCapsuleButtonStyle(color: .appDeepBackground, width: 300)) diff --git a/apple/OmnivoreKit/Sources/App/Views/Registration/RegistrationView.swift b/apple/OmnivoreKit/Sources/App/Views/Registration/RegistrationView.swift index 5d71bf922..863ac8751 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Registration/RegistrationView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Registration/RegistrationView.swift @@ -1,12 +1,11 @@ import AuthenticationServices -import Combine import Models import Services import SwiftUI import Utils import Views -final class RegistrationViewModel: ObservableObject { +@MainActor final class RegistrationViewModel: ObservableObject { enum RegistrationState { case createProfile(userProfile: UserProfile) case newAppleSignUp(userProfile: UserProfile) @@ -15,12 +14,10 @@ final class RegistrationViewModel: ObservableObject { @Published var loginError: LoginError? @Published var registrationState: RegistrationState? - var subscriptions = Set() - func handleAppleSignInCompletion(result: Result, authenticator: Authenticator) { switch AppleSigninPayload.parse(authResult: result) { case let .success(payload): - handleAppleToken(payload: payload, authenticator: authenticator) + Task { await handleAppleToken(payload: payload, authenticator: authenticator) } case let .failure(error): switch error { case .unauthorized, .unknown: @@ -31,39 +28,34 @@ final class RegistrationViewModel: ObservableObject { } } - private func handleAppleToken(payload: AppleSigninPayload, authenticator: Authenticator) { - authenticator.submitAppleToken(token: payload.token).sink( - receiveCompletion: { [weak self] completion in - guard case let .failure(loginError) = completion else { return } - switch loginError { - case .unauthorized, .unknown: - self?.handleAppleSignUp(authenticator: authenticator, payload: payload) - case .network: - self?.loginError = loginError - } - }, - receiveValue: { _ in } - ) - .store(in: &subscriptions) + private func handleAppleToken(payload: AppleSigninPayload, authenticator: Authenticator) async { + do { + try await authenticator.submitAppleToken(token: payload.token) + } catch { + let submitTokenError = (error as? LoginError) ?? .unknown + switch submitTokenError { + case .unauthorized, .unknown: + await handleAppleSignUp(authenticator: authenticator, payload: payload) + case .network: + loginError = submitTokenError + } + } } - private func handleAppleSignUp(authenticator: Authenticator, payload: AppleSigninPayload) { - authenticator - .createPendingAccountUsingApple(token: payload.token, name: payload.fullName) - .sink( - receiveCompletion: { [weak self] completion in - guard case let .failure(loginError) = completion else { return } - self?.loginError = loginError - }, - receiveValue: { [weak self] userProfile in - if userProfile.name.isEmpty { - self?.registrationState = .createProfile(userProfile: userProfile) - } else { - self?.registrationState = .newAppleSignUp(userProfile: userProfile) - } - } + private func handleAppleSignUp(authenticator: Authenticator, payload: AppleSigninPayload) async { + do { + let pendingUserProfile = try await authenticator.createPendingAccountUsingApple( + token: payload.token, + name: payload.fullName ) - .store(in: &subscriptions) + if pendingUserProfile.name.isEmpty { + registrationState = .createProfile(userProfile: pendingUserProfile) + } else { + registrationState = .newAppleSignUp(userProfile: pendingUserProfile) + } + } catch { + loginError = (error as? LoginError) ?? .unknown + } } func handleGoogleAuth(authenticator: Authenticator) async { diff --git a/apple/OmnivoreKit/Sources/Services/Authentication/AccountCreator.swift b/apple/OmnivoreKit/Sources/Services/Authentication/AccountCreator.swift index 3fbef9dc8..a0e044364 100644 --- a/apple/OmnivoreKit/Sources/Services/Authentication/AccountCreator.swift +++ b/apple/OmnivoreKit/Sources/Services/Authentication/AccountCreator.swift @@ -1,4 +1,3 @@ -import Combine import Foundation import Models @@ -6,12 +5,12 @@ public extension Authenticator { func createPendingAccountUsingApple( token: String, name: PersonNameComponents? - ) -> AnyPublisher { + ) async throws -> UserProfile { let params = CreatePendingAccountParams(token: token, provider: .apple, fullName: name) - return createPendingAccount(params: params) + return try await createPendingAccount(params: params) } - func createAccount(userProfile: UserProfile) -> AnyPublisher { + func createAccount(userProfile: UserProfile) async throws { let params = CreateAccountParams( pendingUserToken: pendingUserToken ?? "", userProfile: userProfile @@ -19,36 +18,29 @@ public extension Authenticator { let encodedParams = (try? JSONEncoder().encode(params)) ?? Data() - return networker - .createAccount(params: encodedParams) - .tryMap { [weak self] in - try ValetKey.authCookieString.setValue($0.commentedAuthCookieString) - try ValetKey.authToken.setValue($0.authToken) - self?.pendingUserToken = nil - self?.isLoggedIn = true + do { + let authPayload = try await networker.createAccount(params: encodedParams) + try ValetKey.authCookieString.setValue(authPayload.commentedAuthCookieString) + try ValetKey.authToken.setValue(authPayload.authToken) + DispatchQueue.main.async { + self.pendingUserToken = nil + self.isLoggedIn = true } - .mapError { error in - let serverError = (error as? ServerError) ?? ServerError.unknown - return LoginError.make(serverError: serverError) - } - .eraseToAnyPublisher() + } catch { + let serverError = (error as? ServerError) ?? ServerError.unknown + throw LoginError.make(serverError: serverError) + } } } extension Authenticator { - func createPendingAccount(params: CreatePendingAccountParams) -> AnyPublisher { - let encodedParams = (try? JSONEncoder().encode(params)) ?? Data() - - return networker - .createPendingUserDep(params: encodedParams) - .tryMap { [weak self] in - self?.pendingUserToken = $0.pendingUserToken - return $0.pendingUserProfile - } - .mapError { error in - let serverError = (error as? ServerError) ?? ServerError.unknown - return LoginError.make(serverError: serverError) - } - .eraseToAnyPublisher() + func createPendingAccount(params: CreatePendingAccountParams) async throws -> UserProfile { + do { + let encodedParams = (try? JSONEncoder().encode(params)) ?? Data() + let pendingUserAuthPayload = try await networker.createPendingUser(params: encodedParams) + return pendingUserAuthPayload.pendingUserProfile + } catch { + throw LoginError.make(serverError: (error as? ServerError) ?? .unknown) + } } } diff --git a/apple/OmnivoreKit/Sources/Services/Authentication/AppleAuth.swift b/apple/OmnivoreKit/Sources/Services/Authentication/AppleAuth.swift index 22475dc5e..ae25129e9 100644 --- a/apple/OmnivoreKit/Sources/Services/Authentication/AppleAuth.swift +++ b/apple/OmnivoreKit/Sources/Services/Authentication/AppleAuth.swift @@ -3,18 +3,17 @@ import Foundation import Models public extension Authenticator { - func submitAppleToken(token: String) -> AnyPublisher { - networker - .submitAppleToken(token: token) - .tryMap { [weak self] in - try ValetKey.authCookieString.setValue($0.commentedAuthCookieString) - try ValetKey.authToken.setValue($0.authToken) - self?.isLoggedIn = true + func submitAppleToken(token: String) async throws { + do { + let authPayload = try await networker.submitAppleToken(token: token) + try ValetKey.authCookieString.setValue(authPayload.commentedAuthCookieString) + try ValetKey.authToken.setValue(authPayload.authToken) + DispatchQueue.main.async { + self.isLoggedIn = true } - .mapError { error in - let serverError = (error as? ServerError) ?? ServerError.unknown - return LoginError.make(serverError: serverError) - } - .eraseToAnyPublisher() + } catch { + let serverError = (error as? ServerError) ?? ServerError.unknown + throw LoginError.make(serverError: serverError) + } } } diff --git a/apple/OmnivoreKit/Sources/Services/Authentication/Authenticator.swift b/apple/OmnivoreKit/Sources/Services/Authentication/Authenticator.swift index 354c834ad..cd2f0c66b 100644 --- a/apple/OmnivoreKit/Sources/Services/Authentication/Authenticator.swift +++ b/apple/OmnivoreKit/Sources/Services/Authentication/Authenticator.swift @@ -1,4 +1,3 @@ -import Combine import Foundation import GoogleSignIn import Models @@ -19,11 +18,9 @@ public final class Authenticator: ObservableObject { } @Published public internal(set) var isLoggedIn: Bool - @Published public var pendinguserProfile = UserProfile(username: "", name: "", bio: nil) let networker: Networker - var subscriptions = Set() var pendingUserToken: String? public init(networker: Networker) { diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/CreateAccountResource.swift b/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/CreateAccountResource.swift index 3743c1875..5e0b6f196 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/CreateAccountResource.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/CreateAccountResource.swift @@ -1,8 +1,7 @@ -import Combine import Foundation extension Networker { - func createAccount(params: Data) -> AnyPublisher { + func createAccount(params: Data) async throws -> AuthPayload { let urlRequest = URLRequest.create( baseURL: appEnvironment.serverBaseURL, urlPath: "/api/mobile-auth/create-account", @@ -14,9 +13,10 @@ extension Networker { decode: AuthPayload.decode ) - return urlSession - .performRequest(resource: resource) - .receive(on: DispatchQueue.main) - .eraseToAnyPublisher() + do { + return try await urlSession.performReq(resource: resource) + } catch { + throw (error as? ServerError) ?? .unknown + } } } diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/CreatePendingUserResource.swift b/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/CreatePendingUserResource.swift index f220dc39d..596bca4ca 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/CreatePendingUserResource.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/CreatePendingUserResource.swift @@ -1,27 +1,6 @@ -import Combine import Foundation import Models -extension Networker { - func createPendingUserDep(params: Data) -> AnyPublisher { - let urlRequest = URLRequest.create( - baseURL: appEnvironment.serverBaseURL, - urlPath: "/api/mobile-auth/sign-up", - requestMethod: .post(params: params) - ) - - let resource = ServerResource( - urlRequest: urlRequest, - decode: PendingUserAuthPayload.decode - ) - - return urlSession - .performRequest(resource: resource) - .receive(on: DispatchQueue.main) - .eraseToAnyPublisher() - } -} - extension Networker { func createPendingUser(params: Data) async throws -> PendingUserAuthPayload { let urlRequest = URLRequest.create( diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/VerifyAppleTokenResource.swift b/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/VerifyAppleTokenResource.swift deleted file mode 100644 index e8a681d52..000000000 --- a/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/VerifyAppleTokenResource.swift +++ /dev/null @@ -1,26 +0,0 @@ -import Combine -import Foundation -import Models - -extension Networker { - func submitAppleToken(token: String) -> AnyPublisher { - let params = SignInParams(token: token, provider: .apple) - let encodedParams = (try? JSONEncoder().encode(params)) ?? Data() - - let urlRequest = URLRequest.create( - baseURL: appEnvironment.serverBaseURL, - urlPath: "/api/mobile-auth/sign-in", - requestMethod: .post(params: encodedParams) - ) - - let resource = ServerResource( - urlRequest: urlRequest, - decode: AuthPayload.decode - ) - - return urlSession - .performRequest(resource: resource) - .receive(on: DispatchQueue.main) - .eraseToAnyPublisher() - } -} diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/CreateGoogleToken.swift b/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/VerifyAuthProviderToken.swift similarity index 71% rename from apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/CreateGoogleToken.swift rename to apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/VerifyAuthProviderToken.swift index df241e3a7..52304dfbf 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/CreateGoogleToken.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/VerifyAuthProviderToken.swift @@ -2,8 +2,17 @@ import Foundation import Models extension Networker { + func submitAppleToken(token: String) async throws -> AuthPayload { + let params = SignInParams(token: token, provider: .apple) + return try await submitSignInParams(params: params) + } + func submitGoogleToken(idToken: String) async throws -> AuthPayload { let params = SignInParams(token: idToken, provider: .google) + return try await submitSignInParams(params: params) + } + + func submitSignInParams(params: SignInParams) async throws -> AuthPayload { let encodedParams = (try? JSONEncoder().encode(params)) ?? Data() let urlRequest = URLRequest.create(