convert more combine publishers to async

This commit is contained in:
Satindar Dhillon
2022-06-21 14:38:52 -07:00
parent 30806e4c1b
commit f449f52455
10 changed files with 101 additions and 157 deletions

View File

@ -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) {

View File

@ -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<AnyCancellable>()
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))

View File

@ -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<AnyCancellable>()
func handleAppleSignInCompletion(result: Result<ASAuthorization, Error>, 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 {

View File

@ -1,4 +1,3 @@
import Combine
import Foundation
import Models
@ -6,12 +5,12 @@ public extension Authenticator {
func createPendingAccountUsingApple(
token: String,
name: PersonNameComponents?
) -> AnyPublisher<UserProfile, LoginError> {
) 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<Void, LoginError> {
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<UserProfile, LoginError> {
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)
}
}
}

View File

@ -3,18 +3,17 @@ import Foundation
import Models
public extension Authenticator {
func submitAppleToken(token: String) -> AnyPublisher<Void, LoginError> {
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)
}
}
}

View File

@ -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<AnyCancellable>()
var pendingUserToken: String?
public init(networker: Networker) {

View File

@ -1,8 +1,7 @@
import Combine
import Foundation
extension Networker {
func createAccount(params: Data) -> AnyPublisher<AuthPayload, ServerError> {
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
}
}
}

View File

@ -1,27 +1,6 @@
import Combine
import Foundation
import Models
extension Networker {
func createPendingUserDep(params: Data) -> AnyPublisher<PendingUserAuthPayload, ServerError> {
let urlRequest = URLRequest.create(
baseURL: appEnvironment.serverBaseURL,
urlPath: "/api/mobile-auth/sign-up",
requestMethod: .post(params: params)
)
let resource = ServerResource<PendingUserAuthPayload>(
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(

View File

@ -1,26 +0,0 @@
import Combine
import Foundation
import Models
extension Networker {
func submitAppleToken(token: String) -> AnyPublisher<AuthPayload, ServerError> {
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<AuthPayload>(
urlRequest: urlRequest,
decode: AuthPayload.decode
)
return urlSession
.performRequest(resource: resource)
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
}

View File

@ -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(