Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions DevLog/Data/Common/DataLayerError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import Foundation
enum AuthError: Error {
case notAuthenticated
case failedToUnlinkLastProvider
case linkEmailNotFound
case linkEmailMismatch
case linkCredentialAlreadyInUse
case unsupportedProvider
}

Expand Down
31 changes: 29 additions & 2 deletions DevLog/Data/Repository/AuthDataRepositoryImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,12 @@ final class AuthDataRepositoryImpl: AuthDataRepository {
case .github:
service = githubAuthService
}

try await service.link(uid: uid, email: email)

do {
try await service.link(uid: uid, email: email)
} catch {
throw mapLinkError(error)
}
}

func unlinkProvider(_ provider: AuthProvider) async throws {
Expand All @@ -80,3 +84,26 @@ final class AuthDataRepositoryImpl: AuthDataRepository {
try await service.unlink(uid)
}
}

private extension AuthDataRepositoryImpl {
func mapLinkError(_ error: Error) -> Error {
if let emailFetchError = error as? EmailFetchError {
switch emailFetchError {
case .emailNotFound:
return AuthError.linkEmailNotFound
case .emailMismatch:
return AuthError.linkEmailMismatch
}
}

let nsError = error as NSError
if nsError.domain == AuthErrorDomain,
let authErrorCode = AuthErrorCode(rawValue: nsError.code) {
if authErrorCode == .credentialAlreadyInUse {
return AuthError.linkCredentialAlreadyInUse
}
}

return error
}
}
32 changes: 31 additions & 1 deletion DevLog/Presentation/ViewModel/AccountViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ final class AccountViewModel: Store {
}

enum AlertType {
case linkEmailNotFound
case linkEmailMismatch
case linkCredentialAlreadyInUse
case error
}

Expand Down Expand Up @@ -114,7 +117,8 @@ final class AccountViewModel: Store {
let (currentProvider, allProviders) = try await fetchProvidersUseCase.execute()
send(.updateProviders(currentProvider: currentProvider, allProviders: allProviders))
} catch {
send(.setAlert(isPresented: true, type: .error))
if error.isSocialLoginCancelled { return }
send(.setAlert(isPresented: true, type: linkAlertType(for: error)))
}
}
case .unlink(let provider):
Expand All @@ -137,8 +141,34 @@ final class AccountViewModel: Store {
}

private extension AccountViewModel {
func linkAlertType(for error: Error) -> AlertType {
guard let authError = error as? AuthError else {
return .error
}

switch authError {
case .linkEmailNotFound:
return .linkEmailNotFound
case .linkEmailMismatch:
return .linkEmailMismatch
case .linkCredentialAlreadyInUse:
return .linkCredentialAlreadyInUse
case .notAuthenticated, .failedToUnlinkLastProvider, .unsupportedProvider:
return .error
}
}
Comment on lines +144 to +159
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

현재 switch 문은 명시적으로 처리되지 않은 케이스에 대해 default: break를 사용하여 함수 마지막의 return .error로 처리 흐름을 넘기고 있습니다. AuthError의 모든 케이스를 switch 문 안에서 명시적으로 처리하도록 변경하면, 향후 AuthError에 새로운 케이스가 추가되었을 때 컴파일러가 이를 감지하여 실수를 방지할 수 있습니다. 이는 코드의 안정성을 높이는 데 도움이 됩니다.

Suggested change
func linkAlertType(for error: Error) -> AlertType {
if let authError = error as? AuthError {
switch authError {
case .linkEmailNotFound:
return .linkEmailNotFound
case .linkEmailMismatch:
return .linkEmailMismatch
case .linkCredentialAlreadyInUse:
return .linkCredentialAlreadyInUse
default:
break
}
}
return .error
}
func linkAlertType(for error: Error) -> AlertType {
guard let authError = error as? AuthError else {
return .error
}
switch authError {
case .linkEmailNotFound:
return .linkEmailNotFound
case .linkEmailMismatch:
return .linkEmailMismatch
case .linkCredentialAlreadyInUse:
return .linkCredentialAlreadyInUse
case .notAuthenticated, .failedToUnlinkLastProvider, .unsupportedProvider:
return .error
}
}


func setAlert(_ state: inout State, isPresented: Bool, type: AlertType?) {
switch type {
case .linkEmailNotFound:
state.alertTitle = "이메일 확인 불가"
state.alertMessage = "선택한 계정의 이메일 정보를 확인할 수 없어 연결할 수 없어요. 계정 설정을 확인한 뒤 다시 시도해주세요."
case .linkEmailMismatch:
state.alertTitle = "연결할 수 없음"
state.alertMessage = "현재 로그인한 계정과 선택한 계정의 이메일이 달라 연결할 수 없어요. 같은 이메일의 계정으로 다시 시도해주세요."
case .linkCredentialAlreadyInUse:
state.alertTitle = "이미 연결된 계정"
state.alertMessage = "선택한 계정은 이미 다른 계정에 연결되어 있어요. 해당 계정으로 로그인한 뒤 이용해주세요."
case .error:
state.alertTitle = "오류"
state.alertMessage = "문제가 발생했습니다. 잠시 후 다시 시도해주세요."
Expand Down
Loading