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
15 changes: 0 additions & 15 deletions Application/DevLogData/Sources/Common/DataLayerError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
// Created by opfic on 3/11/26.
//

import AuthenticationServices
import Foundation
import DevLogCore

Expand Down Expand Up @@ -48,17 +47,3 @@ public enum DataLayerError: Error {
case notAuthenticated
case linkCredentialAlreadyInUse
}

public extension Error {
var isSocialLoginCancelled: Bool {
switch self {
case let authError as ASAuthorizationError:
return authError.code == .canceled
case let webAuthError as ASWebAuthenticationSessionError:
return webAuthError.code == .canceledLogin
default:
let nsError = self as NSError
return nsError.domain == "com.google.GIDSignIn" && nsError.code == -5
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
import Foundation

public protocol AuthenticationService {
func signIn() async throws -> AuthDataResponse
func signIn() async throws -> AuthDataResponse?
func signOut(_ uid: String) async throws
func deleteAuth(_ uid: String) async throws
func link(uid: String, email: String) async throws
func link(uid: String, email: String) async throws -> Bool
func unlink(_ uid: String) async throws
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ final class AuthDataRepositoryImpl: AuthDataRepository {
return providerStrings.compactMap { AuthProvider(rawValue: $0) }
}

func linkProvider(_ provider: AuthProvider) async throws {
func linkProvider(_ provider: AuthProvider) async throws -> Bool {
guard let uid = authService.uid,
let email = authService.currentUserEmail else {
throw AuthError.notAuthenticated
Expand All @@ -54,7 +54,7 @@ final class AuthDataRepositoryImpl: AuthDataRepository {
}

do {
try await service.link(uid: uid, email: email)
return try await service.link(uid: uid, email: email)
} catch {
throw mapLinkError(error)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ final class AuthenticationRepositoryImpl: AuthenticationRepository {
self.widgetSnapshotUpdater = widgetSnapshotUpdater
}

func signIn(_ provider: AuthProvider) async throws {
func signIn(_ provider: AuthProvider) async throws -> Bool {
authService.beginSignIn()

do {
let response: AuthDataResponse
let response: AuthDataResponse?
switch provider {
case .apple:
response = try await appleAuthService.signIn()
Expand All @@ -45,8 +45,14 @@ final class AuthenticationRepositoryImpl: AuthenticationRepository {
response = try await googleAuthService.signIn()
}

guard let response else {
authService.cancelSignIn()
return false
}

try await userService.upsertUser(response)
authService.completeSignIn()
return true
} catch {
if authService.uid != nil {
try? await authService.clearCurrentSession()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public protocol AuthDataRepository {
func fetchAllProviders() async throws -> [AuthProvider]

/// 특정 프로바이더를 계정에 연결합니다
func linkProvider(_ provider: AuthProvider) async throws
func linkProvider(_ provider: AuthProvider) async throws -> Bool

/// 특정 프로바이더를 계정에서 해제합니다
func unlinkProvider(_ provider: AuthProvider) async throws
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import Foundation

public protocol AuthenticationRepository {
func signIn(_ provider: AuthProvider) async throws
func signIn(_ provider: AuthProvider) async throws -> Bool
func signOut() async throws
func restore() -> Bool
func delete() async throws
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
//

public protocol LinkAuthProviderUseCase {
func execute(_ provider: AuthProvider) async throws
func execute(_ provider: AuthProvider) async throws -> Bool
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public final class LinkAuthProviderUseCaseImpl: LinkAuthProviderUseCase {
self.repository = repository
}

public func execute(_ provider: AuthProvider) async throws {
public func execute(_ provider: AuthProvider) async throws -> Bool {
try await repository.linkProvider(provider)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
//

public protocol SignInUseCase {
func execute(_ provider: AuthProvider) async throws
func execute(_ provider: AuthProvider) async throws -> Bool
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public final class SignInUseCaseImpl: SignInUseCase {
self.repository = repository
}

public func execute(_ provider: AuthProvider) async throws {
public func execute(_ provider: AuthProvider) async throws -> Bool {
try await repository.signIn(provider)
}
}
14 changes: 14 additions & 0 deletions Application/DevLogInfra/Sources/Common/InfraLayerError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,17 @@ enum SocialLoginError: Error {
case failedToStartWebAuthenticationSession
case authenticationAlreadyInProgress
}

extension Error {
var isSocialLoginCancelled: Bool {
switch self {
case let authError as ASAuthorizationError:
return authError.code == .canceled
case let webAuthError as ASWebAuthenticationSessionError:
return webAuthError.code == .canceledLogin
default:
let nsError = self as NSError
return nsError.domain == "com.google.GIDSignIn" && nsError.code == -5
}
}
}
5 changes: 3 additions & 2 deletions Application/DevLogInfra/Sources/Service/AuthServiceImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,11 @@ final class AuthServiceImpl: AuthService {
logger.info("Clearing current auth session")

do {
try await messaging.deleteToken()
if messaging.fcmToken != nil {
try await messaging.deleteToken()
}
} catch {
logger.error("Failed to delete FCM token while clearing session", error: error)
record(error, code: .deleteMessagingToken)
}

do {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ final class AppleAuthenticationServiceImpl: AuthenticationService {
private let providerID = AuthProviderID.apple
private let logger = Logger(category: "AppleAuthService")

func signIn() async throws -> AuthDataResponse {
func signIn() async throws -> AuthDataResponse? {
logger.info("Starting Apple sign in")

do {
Expand Down Expand Up @@ -105,6 +105,8 @@ final class AppleAuthenticationServiceImpl: AuthenticationService {
logger.info("Successfully signed in with Apple")
return result.user.makeResponse(providerID: .apple)
} catch {
if error.isSocialLoginCancelled { return nil }

logger.error("Failed to sign in with Apple", error: error)
record(error, code: .signIn)
throw error
Expand All @@ -114,14 +116,16 @@ final class AppleAuthenticationServiceImpl: AuthenticationService {
func signOut(_ uid: String) async throws {
do {
let infoRef = store.document(FirestorePath.userData(uid, document: .tokens))
let doc = try await infoRef.getDocument()
try? await infoRef.updateData(["fcmToken": FieldValue.delete()])

if doc.exists {
try await infoRef.updateData(["fcmToken": FieldValue.delete()])
if messaging.fcmToken != nil {
do {
try await messaging.deleteToken()
} catch {
logger.error("Failed to delete FCM token while signing out with Apple", error: error)
}
}
Comment thread
opficdev marked this conversation as resolved.

try await messaging.deleteToken()

try Auth.auth().signOut()
} catch {
logger.error("Failed to sign out with Apple", error: error)
Expand All @@ -142,7 +146,7 @@ final class AppleAuthenticationServiceImpl: AuthenticationService {
}
}

func link(uid: String, email: String) async throws {
func link(uid: String, email: String) async throws -> Bool {
do {
let response = try await authenticateWithAppleAsync()

Expand Down Expand Up @@ -170,7 +174,10 @@ final class AppleAuthenticationServiceImpl: AuthenticationService {
)

try await user?.link(with: appleCredential)
return true
} catch {
if error.isSocialLoginCancelled { return false }

logger.error("Failed to link Apple account", error: error)
record(error, code: .link)
if error.isFirebaseCredentialAlreadyInUse {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ final class GithubAuthenticationServiceImpl: NSObject, AuthenticationService {
)
)

func signIn() async throws -> AuthDataResponse {
func signIn() async throws -> AuthDataResponse? {
logger.info("Starting GitHub sign in")

do {
Expand Down Expand Up @@ -90,6 +90,8 @@ final class GithubAuthenticationServiceImpl: NSObject, AuthenticationService {
accessToken: accessToken
)
} catch {
if error.isSocialLoginCancelled { return nil }

logger.error("Failed to sign in with GitHub", error: error)
record(error, code: .signIn)
throw error
Expand All @@ -99,14 +101,16 @@ final class GithubAuthenticationServiceImpl: NSObject, AuthenticationService {
func signOut(_ uid: String) async throws {
do {
let infoRef = store.document(FirestorePath.userData(uid, document: .tokens))
let doc = try await infoRef.getDocument()
try? await infoRef.updateData(["fcmToken": FieldValue.delete()])

if doc.exists {
try await infoRef.updateData(["fcmToken": FieldValue.delete()])
if messaging.fcmToken != nil {
do {
try await messaging.deleteToken()
} catch {
logger.error("Failed to delete FCM token while signing out with GitHub", error: error)
}
}
Comment thread
opficdev marked this conversation as resolved.

try await messaging.deleteToken()

try Auth.auth().signOut()
} catch {
logger.error("Failed to sign out with GitHub", error: error)
Expand All @@ -125,7 +129,7 @@ final class GithubAuthenticationServiceImpl: NSObject, AuthenticationService {
}
}

func link(uid: String, email: String) async throws {
func link(uid: String, email: String) async throws -> Bool {
logger.info("Linking GitHub account for user: \(uid)")

do {
Expand Down Expand Up @@ -153,7 +157,10 @@ final class GithubAuthenticationServiceImpl: NSObject, AuthenticationService {
try await user?.link(with: credential)

logger.info("Successfully linked GitHub account")
return true
} catch {
if error.isSocialLoginCancelled { return false }

logger.error("Failed to link GitHub account", error: error)
record(error, code: .link)
if error.isFirebaseCredentialAlreadyInUse {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ final class GoogleAuthenticationServiceImpl: AuthenticationService {
private let logger = Logger(category: "GoogleAuthService")

@MainActor
func signIn() async throws -> AuthDataResponse {
func signIn() async throws -> AuthDataResponse? {
logger.info("Starting Google sign in")

guard let topViewController = provider.topViewController() else {
Expand Down Expand Up @@ -66,6 +66,8 @@ final class GoogleAuthenticationServiceImpl: AuthenticationService {
logger.info("Successfully signed in with Google")
return result.user.makeResponse(providerID: .google)
} catch {
if error.isSocialLoginCancelled { return nil }

logger.error("Failed to sign in with Google", error: error)
record(error, code: .signIn)
throw error
Expand All @@ -75,16 +77,18 @@ final class GoogleAuthenticationServiceImpl: AuthenticationService {
func signOut(_ uid: String) async throws {
do {
let infoRef = store.document(FirestorePath.userData(uid, document: .tokens))
let doc = try await infoRef.getDocument()

if doc.exists {
try await infoRef.updateData(["fcmToken": FieldValue.delete()])
}
try? await infoRef.updateData(["fcmToken": FieldValue.delete()])

GIDSignIn.sharedInstance.signOut()
try await GIDSignIn.sharedInstance.disconnect()

try await messaging.deleteToken()
if messaging.fcmToken != nil {
do {
try await messaging.deleteToken()
} catch {
logger.error("Failed to delete FCM token while signing out with Google", error: error)
}
}
Comment thread
opficdev marked this conversation as resolved.

try Auth.auth().signOut()
} catch {
Expand All @@ -106,7 +110,7 @@ final class GoogleAuthenticationServiceImpl: AuthenticationService {
}

@MainActor
func link(uid: String, email: String) async throws {
func link(uid: String, email: String) async throws -> Bool {
do {
guard let topViewController = provider.topViewController() else {
throw UIError.notFoundTopViewController
Expand Down Expand Up @@ -134,7 +138,10 @@ final class GoogleAuthenticationServiceImpl: AuthenticationService {
let credential = GoogleAuthProvider.credential(withIDToken: idToken, accessToken: accessToken)

try await user?.link(with: credential)
return true
} catch {
if error.isSocialLoginCancelled { return false }

logger.error("Failed to link Google account", error: error)
record(error, code: .link)
if error.isFirebaseCredentialAlreadyInUse {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,3 @@
//
// Created by opfic on 5/15/26.
//

import AuthenticationServices
import Foundation

extension Error {
var isSocialLoginCancelled: Bool {
switch self {
case let authError as ASAuthorizationError:
return authError.code == .canceled
case let webAuthError as ASWebAuthenticationSessionError:
return webAuthError.code == .canceledLogin
default:
let nsError = self as NSError
return nsError.domain == "com.google.GIDSignIn" && nsError.code == -5
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,12 @@ private extension LoginFeature {
.run { [signInUseCase] send in
await send(.loading(.begin(target: .default, mode: .immediate)))
do {
try await signInUseCase.execute(provider)
let signedIn = try await signInUseCase.execute(provider)
// 유스케이스 완료가 화면 전환 완료를 의미하지 않으므로 LoginView가 교체될 때까지 로딩을 유지한다.
guard !signedIn else { return }
await send(.loading(.end(target: .default, mode: .immediate)))
} catch {
await send(.loading(.end(target: .default, mode: .immediate)))
if error.isSocialLoginCancelled { return }
await send(.signInFailed(Self.alertType(for: error)))
}
}
Expand Down
Loading
Loading