From b167effde735eccf8162c50c86237f40bb5cb198 Mon Sep 17 00:00:00 2001 From: Melt Date: Sat, 18 Jan 2025 21:38:48 +0900 Subject: [PATCH] =?UTF-8?q?[Feat]=20#468=20-=20Apple=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=B0=9B=EC=9D=80=20identityToken=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=A9=94=EC=9D=B4=EC=BB=A4=EC=8A=A4=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20api=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Literals/UserDefaultKeyLIst.swift | 11 +++++ .../Repository/CoreAuthRepository.swift | 49 +++++++++++++------ .../Repository/CoreOAuthRepository.swift | 33 +++++++++++++ .../Sources/Transform/LoginTransform.swift | 31 ++++++++++++ .../Dependency/RegisterDependencies.swift | 7 +++ .../Domain/Sources/Error/CoreAuthError.swift | 5 ++ .../Domain/Sources/Model/CoreAuthTokens.swift | 19 +++++++ .../AuthRepositoryInterface.swift | 19 ------- .../CoreAuthRepositoryInterface.swift | 17 +++++++ .../Sources/UseCase/SignInUseCase.swift | 35 +++++++++---- .../Sources/Coordinator/AuthBuilder.swift | 7 ++- .../ViewModel/SignInViewModel.swift | 10 +++- .../Networks/Sources/API/CoreAuthAPI.swift | 2 +- ...ity.swift => CoreLoginRequestEntity.swift} | 8 +-- .../Entity/CoreAuth/CoreSignInEntity.swift | 8 +-- .../Sources/Service/CoreAuthService.swift | 4 +- .../Dependency/RegisterDependencies.swift | 7 +++ 17 files changed, 215 insertions(+), 57 deletions(-) create mode 100644 SOPT-iOS/Projects/Data/Sources/Repository/CoreOAuthRepository.swift create mode 100644 SOPT-iOS/Projects/Data/Sources/Transform/LoginTransform.swift create mode 100644 SOPT-iOS/Projects/Domain/Sources/Model/CoreAuthTokens.swift delete mode 100644 SOPT-iOS/Projects/Domain/Sources/RepositoryInterface/AuthRepositoryInterface.swift create mode 100644 SOPT-iOS/Projects/Domain/Sources/RepositoryInterface/CoreAuthRepositoryInterface.swift rename SOPT-iOS/Projects/Modules/Networks/Sources/Entity/CoreAuth/{LoginEntity.swift => CoreLoginRequestEntity.swift} (60%) diff --git a/SOPT-iOS/Projects/Core/Sources/Literals/UserDefaultKeyLIst.swift b/SOPT-iOS/Projects/Core/Sources/Literals/UserDefaultKeyLIst.swift index f535d687..dcef137c 100644 --- a/SOPT-iOS/Projects/Core/Sources/Literals/UserDefaultKeyLIst.swift +++ b/SOPT-iOS/Projects/Core/Sources/Literals/UserDefaultKeyLIst.swift @@ -9,6 +9,12 @@ import Foundation public struct UserDefaultKeyList { + + public struct CoreAuth { + @UserDefaultWrapper(key: "accessToken") public static var accessToken + @UserDefaultWrapper(key: "refreshToken") public static var refreshToken + } + public struct Auth { @UserDefaultWrapper(key: "appAccessToken") public static var appAccessToken @UserDefaultWrapper(key: "appRefreshToken") public static var appRefreshToken @@ -48,6 +54,11 @@ extension UserDefaultKeyList { UserDefaultKeyList.Auth.isActiveUser = nil } + public static func clearCoreUserData() { + UserDefaultKeyList.CoreAuth.accessToken = nil + UserDefaultKeyList.CoreAuth.refreshToken = nil + } + public static func clearPushToken() { UserDefaultKeyList.User.pushToken = nil } diff --git a/SOPT-iOS/Projects/Data/Sources/Repository/CoreAuthRepository.swift b/SOPT-iOS/Projects/Data/Sources/Repository/CoreAuthRepository.swift index d50a49db..40f8562f 100644 --- a/SOPT-iOS/Projects/Data/Sources/Repository/CoreAuthRepository.swift +++ b/SOPT-iOS/Projects/Data/Sources/Repository/CoreAuthRepository.swift @@ -1,5 +1,5 @@ // -// CoreOAuthRepository.swift +// CoreAuthRepository.swift // Data // // Created by 장석우 on 1/18/25. @@ -9,25 +9,46 @@ import Combine import Foundation +import Core import Domain import Networks -public class CoreOAuthRepository { +public struct CoreAuthRepository { + private let coreAuthService: CoreAuthService - private let appleService: AuthenticationService -// private let googleService: - - public init(appleService: AuthenticationService) { - self.appleService = appleService + public init(coreAuthService: CoreAuthService) { + self.coreAuthService = coreAuthService } } -extension CoreOAuthRepository: CoreOAuthRepositoryInterface { - public func getIdentityToken(from provider: OAuthType) -> AnyPublisher { - switch provider { - case .apple: return appleService.getIdentityToken() - case .google: fatalError() //TODO: - } - +extension CoreAuthRepository: CoreAuthRepositoryInterface { + + public func login( + for provider: OAuthType, + with identityToken: String + ) -> AnyPublisher { + coreAuthService + .login(.dto(token: identityToken, oauthType: provider)) + .compactMap { $0.data?.toDomain() } + .mapError { _ in CoreAuthError.makers(.loginFail) } + .eraseToAnyPublisher() + } + + public func saveTokens(_ tokens: Domain.CoreAuthTokens) { + UserDefaultKeyList.CoreAuth.accessToken = tokens.accessToken + UserDefaultKeyList.CoreAuth.refreshToken = tokens.refreshToken + } + + public func changeSocialAccount() -> AnyPublisher { + fatalError() + } + + public func searchSocialAccount() -> AnyPublisher { + fatalError() + } + + public func signUp(_ model: Domain.SignUpModel) -> AnyPublisher { + fatalError() } } + diff --git a/SOPT-iOS/Projects/Data/Sources/Repository/CoreOAuthRepository.swift b/SOPT-iOS/Projects/Data/Sources/Repository/CoreOAuthRepository.swift new file mode 100644 index 00000000..d50a49db --- /dev/null +++ b/SOPT-iOS/Projects/Data/Sources/Repository/CoreOAuthRepository.swift @@ -0,0 +1,33 @@ +// +// CoreOAuthRepository.swift +// Data +// +// Created by 장석우 on 1/18/25. +// Copyright © 2025 SOPT-iOS. All rights reserved. +// + +import Combine +import Foundation + +import Domain +import Networks + +public class CoreOAuthRepository { + + private let appleService: AuthenticationService +// private let googleService: + + public init(appleService: AuthenticationService) { + self.appleService = appleService + } +} + +extension CoreOAuthRepository: CoreOAuthRepositoryInterface { + public func getIdentityToken(from provider: OAuthType) -> AnyPublisher { + switch provider { + case .apple: return appleService.getIdentityToken() + case .google: fatalError() //TODO: + } + + } +} diff --git a/SOPT-iOS/Projects/Data/Sources/Transform/LoginTransform.swift b/SOPT-iOS/Projects/Data/Sources/Transform/LoginTransform.swift new file mode 100644 index 00000000..465d3a97 --- /dev/null +++ b/SOPT-iOS/Projects/Data/Sources/Transform/LoginTransform.swift @@ -0,0 +1,31 @@ +// +// LoginTransform.swift +// Data +// +// Created by 장석우 on 1/18/25. +// Copyright © 2025 SOPT-iOS. All rights reserved. +// + +import Foundation + +import Domain +import Networks + +extension CoreLoginRequestEntity { + static func dto(token: String, oauthType: OAuthType) -> Self { + var platform: PlatformType + + switch oauthType { + case .apple: platform = .apple + case .google: platform = .google + } + + return .init(token: token, authPlatform: platform) + } +} + +extension CoreLoginEntity { + public func toDomain() -> CoreAuthTokens { + .init(accessToken: accessToken, refreshToken: refreshToken) + } +} diff --git a/SOPT-iOS/Projects/Demo/Sources/Dependency/RegisterDependencies.swift b/SOPT-iOS/Projects/Demo/Sources/Dependency/RegisterDependencies.swift index 177df345..1e781efb 100644 --- a/SOPT-iOS/Projects/Demo/Sources/Dependency/RegisterDependencies.swift +++ b/SOPT-iOS/Projects/Demo/Sources/Dependency/RegisterDependencies.swift @@ -35,6 +35,13 @@ extension AppDelegate { } ) + container.register( + interface: CoreAuthRepositoryInterface.self, + implement: { + CoreAuthRepository(coreAuthService: DefaultCoreAuthService()) + } + ) + container.register( interface: SplashRepositoryInterface.self, implement: { diff --git a/SOPT-iOS/Projects/Domain/Sources/Error/CoreAuthError.swift b/SOPT-iOS/Projects/Domain/Sources/Error/CoreAuthError.swift index c6aca8e5..45caac5e 100644 --- a/SOPT-iOS/Projects/Domain/Sources/Error/CoreAuthError.swift +++ b/SOPT-iOS/Projects/Domain/Sources/Error/CoreAuthError.swift @@ -11,6 +11,7 @@ import Foundation public enum CoreAuthError: Error { case apple(Apple) case google(Google) + case makers(Makers) case unknown(Error) public enum Apple: Error { @@ -22,4 +23,8 @@ public enum CoreAuthError: Error { public enum Google: Error { } + + public enum Makers: Error { + case loginFail + } } diff --git a/SOPT-iOS/Projects/Domain/Sources/Model/CoreAuthTokens.swift b/SOPT-iOS/Projects/Domain/Sources/Model/CoreAuthTokens.swift new file mode 100644 index 00000000..8a41ab95 --- /dev/null +++ b/SOPT-iOS/Projects/Domain/Sources/Model/CoreAuthTokens.swift @@ -0,0 +1,19 @@ +// +// CoreAuthTokens.swift +// Domain +// +// Created by 장석우 on 1/18/25. +// Copyright © 2025 SOPT-iOS. All rights reserved. +// + +import Foundation + +public struct CoreAuthTokens { + public let accessToken: String + public let refreshToken: String + + public init(accessToken: String, refreshToken: String) { + self.accessToken = accessToken + self.refreshToken = refreshToken + } +} diff --git a/SOPT-iOS/Projects/Domain/Sources/RepositoryInterface/AuthRepositoryInterface.swift b/SOPT-iOS/Projects/Domain/Sources/RepositoryInterface/AuthRepositoryInterface.swift deleted file mode 100644 index 2a4473d1..00000000 --- a/SOPT-iOS/Projects/Domain/Sources/RepositoryInterface/AuthRepositoryInterface.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// AuthRepositoryInterface.swift -// Domain -// -// Created by 장석우 on 12/20/24. -// Copyright © 2024 SOPT-iOS. All rights reserved. -// - -import Combine - -// Apple, Google, Makers를 의존하지 않아도 소셜 로그인을 수행할 수 있도록 추상화 -// Apple, Google, Makers 의존은 DefaultAuthRepository에서 수행 - -public protocol AuthRepositoryInterface { - func requestSignIn(token: String) -> AnyPublisher - func oauthLogin(_ type: OAuthType) -> AnyPublisher - func changeSocialAccount() -> AnyPublisher - func signUp(_ model: SignUpModel) -> AnyPublisher -} diff --git a/SOPT-iOS/Projects/Domain/Sources/RepositoryInterface/CoreAuthRepositoryInterface.swift b/SOPT-iOS/Projects/Domain/Sources/RepositoryInterface/CoreAuthRepositoryInterface.swift new file mode 100644 index 00000000..9772b682 --- /dev/null +++ b/SOPT-iOS/Projects/Domain/Sources/RepositoryInterface/CoreAuthRepositoryInterface.swift @@ -0,0 +1,17 @@ +// +// CoreAuthRepositoryInterface.swift +// Domain +// +// Created by 장석우 on 12/20/24. +// Copyright © 2024 SOPT-iOS. All rights reserved. +// + +import Combine + +public protocol CoreAuthRepositoryInterface { + func login(for provider: OAuthType,with identityToken: String) -> AnyPublisher + func changeSocialAccount() -> AnyPublisher + func searchSocialAccount() -> AnyPublisher + func signUp(_ model: SignUpModel) -> AnyPublisher + func saveTokens(_ tokens: CoreAuthTokens) -> Void +} diff --git a/SOPT-iOS/Projects/Domain/Sources/UseCase/SignInUseCase.swift b/SOPT-iOS/Projects/Domain/Sources/UseCase/SignInUseCase.swift index decf52bc..4c3e21d4 100644 --- a/SOPT-iOS/Projects/Domain/Sources/UseCase/SignInUseCase.swift +++ b/SOPT-iOS/Projects/Domain/Sources/UseCase/SignInUseCase.swift @@ -17,7 +17,9 @@ public enum SiginInHandleableType { public protocol SignInUseCase { func requestSignIn(token: String) - func login(with provider: OAuthType) -> AnyPublisher + func login(with provider: OAuthType) -> AnyPublisher + + var sideEffect: PassthroughSubject { get } var signInSuccess: CurrentValueSubject { get set } } @@ -25,21 +27,42 @@ public class DefaultSignInUseCase { private let repository: SignInRepositoryInterface private let oauthRepository: CoreOAuthRepositoryInterface + private let coreRepository: CoreAuthRepositoryInterface private var cancelBag = CancelBag() + public var sideEffect = PassthroughSubject() public var signInSuccess = CurrentValueSubject(.loginFailure) public init( repository: SignInRepositoryInterface, - oauthRepository: CoreOAuthRepositoryInterface + oauthRepository: CoreOAuthRepositoryInterface, + coreRepository: CoreAuthRepositoryInterface ) { self.repository = repository self.oauthRepository = oauthRepository + self.coreRepository = coreRepository } } +//MARK: - 인증중앙화(CoreAuth) 로직 extension DefaultSignInUseCase: SignInUseCase { + public func login(with provider: OAuthType) -> AnyPublisher { + oauthRepository.getIdentityToken(from: .apple) + .map { (provider, $0) } + .flatMap(coreRepository.login) + .handleEvents(receiveOutput: coreRepository.saveTokens) + .mapVoid() + .catch { [weak self] in + self?.sideEffect.send($0) + return Empty() + } + .eraseToAnyPublisher() + } +} + +//MARK: - LegacyAuth 로직 +extension DefaultSignInUseCase { public func requestSignIn(token: String) { repository.requestSignIn(token: token) .sink { event in @@ -54,12 +77,4 @@ extension DefaultSignInUseCase: SignInUseCase { self.signInSuccess.send(isSuccessed ? .loginSuccess : .loginFailure) }.store(in: self.cancelBag) } - - public func login(with provider: OAuthType) -> AnyPublisher { - oauthRepository.getIdentityToken(from: .apple) - .catch { _ in - return Empty() - } - .eraseToAnyPublisher() - } } diff --git a/SOPT-iOS/Projects/Features/AuthFeature/Sources/Coordinator/AuthBuilder.swift b/SOPT-iOS/Projects/Features/AuthFeature/Sources/Coordinator/AuthBuilder.swift index 65011677..332ae811 100644 --- a/SOPT-iOS/Projects/Features/AuthFeature/Sources/Coordinator/AuthBuilder.swift +++ b/SOPT-iOS/Projects/Features/AuthFeature/Sources/Coordinator/AuthBuilder.swift @@ -14,6 +14,7 @@ public final class AuthBuilder { @Injected public var repository: SignInRepositoryInterface @Injected public var oauthRepository: CoreOAuthRepositoryInterface + @Injected public var coreRepository: CoreAuthRepositoryInterface public init() { } } @@ -21,7 +22,11 @@ final class AuthBuilder { extension AuthBuilder: AuthFeatureViewBuildable { public func makeSignIn() -> SignInPresentable { - let useCase = DefaultSignInUseCase(repository: repository, oauthRepository: oauthRepository) + let useCase = DefaultSignInUseCase( + repository: repository, + oauthRepository: oauthRepository, + coreRepository: coreRepository + ) let vm = SignInViewModel(useCase: useCase) let vc = SignInVC() vc.viewModel = vm diff --git a/SOPT-iOS/Projects/Features/AuthFeature/Sources/SignInScene/ViewModel/SignInViewModel.swift b/SOPT-iOS/Projects/Features/AuthFeature/Sources/SignInScene/ViewModel/SignInViewModel.swift index 6d9c75be..b217312e 100644 --- a/SOPT-iOS/Projects/Features/AuthFeature/Sources/SignInScene/ViewModel/SignInViewModel.swift +++ b/SOPT-iOS/Projects/Features/AuthFeature/Sources/SignInScene/ViewModel/SignInViewModel.swift @@ -65,8 +65,8 @@ extension SignInViewModel { input.appleLoginButtonTapped.map { OAuthType.apple } ).flatMap(useCase.login) .withUnretained(self) - .sink { owner, id in - print(id) + .sink { owner, _ in + print("로그인 성공") }.store(in: self.cancelBag) input.signUpButtonTapped @@ -104,5 +104,11 @@ extension SignInViewModel { } receiveValue: { (owner, isSignInSuccess) in owner.onSignInSuccess?(isSignInSuccess) }.store(in: self.cancelBag) + + useCase.sideEffect + .sink { event in + print(event) + } + .store(in: self.cancelBag) } } diff --git a/SOPT-iOS/Projects/Modules/Networks/Sources/API/CoreAuthAPI.swift b/SOPT-iOS/Projects/Modules/Networks/Sources/API/CoreAuthAPI.swift index 2764cd3e..a520d9ac 100644 --- a/SOPT-iOS/Projects/Modules/Networks/Sources/API/CoreAuthAPI.swift +++ b/SOPT-iOS/Projects/Modules/Networks/Sources/API/CoreAuthAPI.swift @@ -12,7 +12,7 @@ public enum CoreAuthAPI { case sendVerifyCode(dto: SendVerificationCodeEntity) case verfiyCode(dto: VerifyCodeEntity) case signUp(dto: SignUpEntity) - case login(dto: LoginEntity) + case login(dto: CoreLoginRequestEntity) } extension CoreAuthAPI: BaseAPI { diff --git a/SOPT-iOS/Projects/Modules/Networks/Sources/Entity/CoreAuth/LoginEntity.swift b/SOPT-iOS/Projects/Modules/Networks/Sources/Entity/CoreAuth/CoreLoginRequestEntity.swift similarity index 60% rename from SOPT-iOS/Projects/Modules/Networks/Sources/Entity/CoreAuth/LoginEntity.swift rename to SOPT-iOS/Projects/Modules/Networks/Sources/Entity/CoreAuth/CoreLoginRequestEntity.swift index 6c87cbf3..0f4381ae 100644 --- a/SOPT-iOS/Projects/Modules/Networks/Sources/Entity/CoreAuth/LoginEntity.swift +++ b/SOPT-iOS/Projects/Modules/Networks/Sources/Entity/CoreAuth/CoreLoginRequestEntity.swift @@ -8,12 +8,12 @@ import Foundation -public struct LoginEntity: Encodable { - let code: String +public struct CoreLoginRequestEntity: Encodable { + let token: String let authPlatform: PlatformType - public init(code: String, authPlatform: PlatformType) { - self.code = code + public init(token: String, authPlatform: PlatformType) { + self.token = token self.authPlatform = authPlatform } } diff --git a/SOPT-iOS/Projects/Modules/Networks/Sources/Entity/CoreAuth/CoreSignInEntity.swift b/SOPT-iOS/Projects/Modules/Networks/Sources/Entity/CoreAuth/CoreSignInEntity.swift index 0f401d5e..270d7259 100644 --- a/SOPT-iOS/Projects/Modules/Networks/Sources/Entity/CoreAuth/CoreSignInEntity.swift +++ b/SOPT-iOS/Projects/Modules/Networks/Sources/Entity/CoreAuth/CoreSignInEntity.swift @@ -1,5 +1,5 @@ // -// CoreSignInEntity.swift +// CoreLoginEntity.swift // Networks // // Created by 장석우 on 12/30/24. @@ -8,9 +8,9 @@ import Foundation -public struct CoreSignInEntity: Decodable { - let accessToken: String - let refreshToken: String +public struct CoreLoginEntity: Decodable { + public let accessToken: String + public let refreshToken: String public init(accessToken: String, refreshToken: String) { self.accessToken = accessToken diff --git a/SOPT-iOS/Projects/Modules/Networks/Sources/Service/CoreAuthService.swift b/SOPT-iOS/Projects/Modules/Networks/Sources/Service/CoreAuthService.swift index 8a1a9cf6..355c0fad 100644 --- a/SOPT-iOS/Projects/Modules/Networks/Sources/Service/CoreAuthService.swift +++ b/SOPT-iOS/Projects/Modules/Networks/Sources/Service/CoreAuthService.swift @@ -18,7 +18,7 @@ public typealias DefaultCoreAuthService = BaseService public protocol CoreAuthService { func sendVerifyCode(_ dto: SendVerificationCodeEntity) -> AnyPublisher func verifyCode(_ dto: VerifyCodeEntity) -> AnyPublisher, Error> - func login(_ dto: LoginEntity) -> AnyPublisher, Error> + func login(_ dto: CoreLoginRequestEntity) -> AnyPublisher, Error> } extension DefaultCoreAuthService: CoreAuthService { @@ -30,7 +30,7 @@ extension DefaultCoreAuthService: CoreAuthService { requestObjectInCombine(.verfiyCode(dto: dto)) } - public func login(_ dto: LoginEntity) -> AnyPublisher, Error> { + public func login(_ dto: CoreLoginRequestEntity) -> AnyPublisher, Error> { requestObjectInCombine(.login(dto: dto)) } } diff --git a/SOPT-iOS/Projects/SOPT-iOS/Sources/Dependency/RegisterDependencies.swift b/SOPT-iOS/Projects/SOPT-iOS/Sources/Dependency/RegisterDependencies.swift index caf1ae8f..33bda065 100644 --- a/SOPT-iOS/Projects/SOPT-iOS/Sources/Dependency/RegisterDependencies.swift +++ b/SOPT-iOS/Projects/SOPT-iOS/Sources/Dependency/RegisterDependencies.swift @@ -35,6 +35,13 @@ extension AppDelegate { } ) + container.register( + interface: CoreAuthRepositoryInterface.self, + implement: { + CoreAuthRepository(coreAuthService: DefaultCoreAuthService()) + } + ) + container.register( interface: SplashRepositoryInterface.self, implement: {