From 47231c26e12dcecdc00ec87bdd1f64250030d60e Mon Sep 17 00:00:00 2001 From: Park Seo Yeon Date: Wed, 29 Jan 2025 05:12:16 +0900 Subject: [PATCH 1/9] =?UTF-8?q?[Feat]=20userUseCase=EC=97=90=20=EC=86=8C?= =?UTF-8?q?=EC=85=9C=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DIContainer/Sources/DIContainer.swift | 9 +++ .../SocialLogInRepositoryImpl.swift | 71 +++++++++++++++++++ .../Service/User/UserRepositoryImpl.swift | 38 +++++++++- .../Common/OnboardingFeature.swift | 8 +++ .../Common/OnboardingView.swift | 0 .../Common/TermFeature.swift | 0 .../Common/TermView.swift | 0 .../Code/MakeInvitationCodeFeature.swift | 0 .../Login/Code/MakeInvitationCodeView.swift | 0 .../ConnectedTraineeProfileFeature.swift | 0 .../ConnectedTraineeProfileView.swift | 0 .../ConnectionCompleteFeature.swift | 0 .../Connection/ConnectionCompleteView.swift | 0 .../TrainerSignUpCompleteFeature.swift | 0 .../Profile/TrainerSignUpCompleteView.swift | 0 15 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 TnT/Projects/Data/Sources/Network/Service/SocialLogin/SocialLogInRepositoryImpl.swift rename TnT/Projects/Presentation/Sources/{Onbarding => Onboarding}/Common/OnboardingFeature.swift (95%) rename TnT/Projects/Presentation/Sources/{Onbarding => Onboarding}/Common/OnboardingView.swift (100%) rename TnT/Projects/Presentation/Sources/{Onbarding => Onboarding}/Common/TermFeature.swift (100%) rename TnT/Projects/Presentation/Sources/{Onbarding => Onboarding}/Common/TermView.swift (100%) rename TnT/Projects/Presentation/Sources/{Onbarding => Onboarding}/Trainer/Login/Code/MakeInvitationCodeFeature.swift (100%) rename TnT/Projects/Presentation/Sources/{Onbarding => Onboarding}/Trainer/Login/Code/MakeInvitationCodeView.swift (100%) rename TnT/Projects/Presentation/Sources/{Onbarding => Onboarding}/Trainer/Login/Connection/ConnectedTraineeProfileFeature.swift (100%) rename TnT/Projects/Presentation/Sources/{Onbarding => Onboarding}/Trainer/Login/Connection/ConnectedTraineeProfileView.swift (100%) rename TnT/Projects/Presentation/Sources/{Onbarding => Onboarding}/Trainer/Login/Connection/ConnectionCompleteFeature.swift (100%) rename TnT/Projects/Presentation/Sources/{Onbarding => Onboarding}/Trainer/Login/Connection/ConnectionCompleteView.swift (100%) rename TnT/Projects/Presentation/Sources/{Onbarding => Onboarding}/Trainer/Login/Profile/TrainerSignUpCompleteFeature.swift (100%) rename TnT/Projects/Presentation/Sources/{Onbarding => Onboarding}/Trainer/Login/Profile/TrainerSignUpCompleteView.swift (100%) diff --git a/TnT/Projects/DIContainer/Sources/DIContainer.swift b/TnT/Projects/DIContainer/Sources/DIContainer.swift index 3f1997e5..6fb9b504 100644 --- a/TnT/Projects/DIContainer/Sources/DIContainer.swift +++ b/TnT/Projects/DIContainer/Sources/DIContainer.swift @@ -19,6 +19,10 @@ private enum TraineeUseCaseKey: DependencyKey { static let liveValue: TraineeUseCase = DefaultTraineeUseCase(trainerRepository: TrainerRepositoryImpl()) } +private enum SocialUseCaseKey: DependencyKey { + static let liveValue: SocialLoginUserCase = DefaultSocialLoginUserCase(socialLoginRepository: SocialLogInRepositoryImpl()) +} + // MARK: - DependencyValues public extension DependencyValues { var userUseCase: UserUseCase { @@ -30,4 +34,9 @@ public extension DependencyValues { get { self[TraineeUseCaseKey.self] } set { self[TraineeUseCaseKey.self] = newValue } } + + var socialLogInUseCase: SocialLoginUserCase { + get { self[SocialUseCaseKey.self] } + set { self[SocialUseCaseKey.self] = newValue } + } } diff --git a/TnT/Projects/Data/Sources/Network/Service/SocialLogin/SocialLogInRepositoryImpl.swift b/TnT/Projects/Data/Sources/Network/Service/SocialLogin/SocialLogInRepositoryImpl.swift new file mode 100644 index 00000000..0e0295c1 --- /dev/null +++ b/TnT/Projects/Data/Sources/Network/Service/SocialLogin/SocialLogInRepositoryImpl.swift @@ -0,0 +1,71 @@ +// +// SocialLogInRepositoryImpl.swift +// Data +// +// Created by 박서연 on 1/29/25. +// Copyright © 2025 yapp25thTeamTnT. All rights reserved. +// + +import Foundation +import Dependencies + +import Domain + +public enum LoginError: Error { + case invalidCredentials + case networkFailure + case kakaoError + case appleError + case unknown(message: String) +} + +//public struct SocialLogInRepositoryImpl: UserRepository { +// +// public let loginManager = SNSLoginManager() +// +// public init() {} +// +// public func appleLoginResult() async throws -> Result { +// let result = await loginManager.appleLogin() +// +// guard let result else { +// return .failure(LoginError.appleError) +// } +// +// let appleRequest = PostSocialLoginReqDTO( +// socialType: "APPLE", +// fcmToken: "", +// socialAccessToken: "", +// authorizationCode: result.authorizationCode, +// idToken: result.identityToken +// ) +// +// return .success(appleRequest) +// } +// +// public func kakaoLoginResult() async throws -> Result { +// let result = await loginManager.kakaoLogin() +// +// guard let result else { +// return .failure(LoginError.kakaoError) +// } +// +// let kakaoRequest = PostSocialLoginReqDTO( +// socialType: "KAKAO", +// fcmToken: "", +// socialAccessToken: result.accessToken, +// authorizationCode: "", +// idToken: "" +// ) +// +// return .success(kakaoRequest) +// } +// +// public func postSocialLogin(_ reqDTO: Domain.PostSocialLoginReqDTO) async throws -> PostSocialLoginResDTO { +// +// } +// +// public func postSignUp(_ reqDTO: Domain.PostSignUpReqDTO, profileImage: Data?) async throws -> PostSignUpResDTO { +// <#code#> +// } +//} diff --git a/TnT/Projects/Data/Sources/Network/Service/User/UserRepositoryImpl.swift b/TnT/Projects/Data/Sources/Network/Service/User/UserRepositoryImpl.swift index 34ef8b28..23746e17 100644 --- a/TnT/Projects/Data/Sources/Network/Service/User/UserRepositoryImpl.swift +++ b/TnT/Projects/Data/Sources/Network/Service/User/UserRepositoryImpl.swift @@ -13,7 +13,7 @@ import Domain /// 사용자 관련 네트워크 요청을 처리하는 UserRepository 구현체 public struct UserRepositoryImpl: UserRepository { - + private let loginManager = SNSLoginManager() private let networkService: NetworkService = .shared public init() {} @@ -36,4 +36,40 @@ public struct UserRepositoryImpl: UserRepository { decodingType: PostSignUpResDTO.self ) } + + public func appleLoginResult() async throws -> Result { + let result = await loginManager.appleLogin() + + guard let result else { + return .failure(LoginError.appleError) + } + + let appleRequest = PostSocialLoginReqDTO( + socialType: "APPLE", + fcmToken: "", + socialAccessToken: "", + authorizationCode: result.authorizationCode, + idToken: result.identityToken + ) + + return .success(appleRequest) + } + + public func kakaoLoginResult() async throws -> Result { + let result = await loginManager.kakaoLogin() + + guard let result else { + return .failure(LoginError.kakaoError) + } + + let kakaoRequest = PostSocialLoginReqDTO( + socialType: "KAKAO", + fcmToken: "", + socialAccessToken: result.accessToken, + authorizationCode: "", + idToken: "" + ) + + return .success(kakaoRequest) + } } diff --git a/TnT/Projects/Presentation/Sources/Onbarding/Common/OnboardingFeature.swift b/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingFeature.swift similarity index 95% rename from TnT/Projects/Presentation/Sources/Onbarding/Common/OnboardingFeature.swift rename to TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingFeature.swift index 7ce8974c..1294e622 100644 --- a/TnT/Projects/Presentation/Sources/Onbarding/Common/OnboardingFeature.swift +++ b/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingFeature.swift @@ -9,11 +9,19 @@ import ComposableArchitecture import SwiftUI +import Domain +import DIContainer + @Reducer public struct OnboardingFeature { @ObservableState public struct State: Equatable { public var path = StackState() + public var userType: UserType? + public var nickname: String? = "" + public var socialType: LoginType? + public var termAgree: Bool = true + public var socialEmail: String? = "" public init(path: StackState = StackState()) { self.path = path diff --git a/TnT/Projects/Presentation/Sources/Onbarding/Common/OnboardingView.swift b/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingView.swift similarity index 100% rename from TnT/Projects/Presentation/Sources/Onbarding/Common/OnboardingView.swift rename to TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingView.swift diff --git a/TnT/Projects/Presentation/Sources/Onbarding/Common/TermFeature.swift b/TnT/Projects/Presentation/Sources/Onboarding/Common/TermFeature.swift similarity index 100% rename from TnT/Projects/Presentation/Sources/Onbarding/Common/TermFeature.swift rename to TnT/Projects/Presentation/Sources/Onboarding/Common/TermFeature.swift diff --git a/TnT/Projects/Presentation/Sources/Onbarding/Common/TermView.swift b/TnT/Projects/Presentation/Sources/Onboarding/Common/TermView.swift similarity index 100% rename from TnT/Projects/Presentation/Sources/Onbarding/Common/TermView.swift rename to TnT/Projects/Presentation/Sources/Onboarding/Common/TermView.swift diff --git a/TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Code/MakeInvitationCodeFeature.swift b/TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Code/MakeInvitationCodeFeature.swift similarity index 100% rename from TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Code/MakeInvitationCodeFeature.swift rename to TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Code/MakeInvitationCodeFeature.swift diff --git a/TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Code/MakeInvitationCodeView.swift b/TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Code/MakeInvitationCodeView.swift similarity index 100% rename from TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Code/MakeInvitationCodeView.swift rename to TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Code/MakeInvitationCodeView.swift diff --git a/TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Connection/ConnectedTraineeProfileFeature.swift b/TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Connection/ConnectedTraineeProfileFeature.swift similarity index 100% rename from TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Connection/ConnectedTraineeProfileFeature.swift rename to TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Connection/ConnectedTraineeProfileFeature.swift diff --git a/TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Connection/ConnectedTraineeProfileView.swift b/TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Connection/ConnectedTraineeProfileView.swift similarity index 100% rename from TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Connection/ConnectedTraineeProfileView.swift rename to TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Connection/ConnectedTraineeProfileView.swift diff --git a/TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Connection/ConnectionCompleteFeature.swift b/TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Connection/ConnectionCompleteFeature.swift similarity index 100% rename from TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Connection/ConnectionCompleteFeature.swift rename to TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Connection/ConnectionCompleteFeature.swift diff --git a/TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Connection/ConnectionCompleteView.swift b/TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Connection/ConnectionCompleteView.swift similarity index 100% rename from TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Connection/ConnectionCompleteView.swift rename to TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Connection/ConnectionCompleteView.swift diff --git a/TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Profile/TrainerSignUpCompleteFeature.swift b/TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Profile/TrainerSignUpCompleteFeature.swift similarity index 100% rename from TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Profile/TrainerSignUpCompleteFeature.swift rename to TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Profile/TrainerSignUpCompleteFeature.swift diff --git a/TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Profile/TrainerSignUpCompleteView.swift b/TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Profile/TrainerSignUpCompleteView.swift similarity index 100% rename from TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Profile/TrainerSignUpCompleteView.swift rename to TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Profile/TrainerSignUpCompleteView.swift From 5fa6faf5603beb3268cc9b1342fe1a6c5f49ec30 Mon Sep 17 00:00:00 2001 From: Park Seo Yeon Date: Wed, 29 Jan 2025 19:48:29 +0900 Subject: [PATCH 2/9] =?UTF-8?q?[Feat]=20Firebase=20Info=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TnT/.gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TnT/.gitignore b/TnT/.gitignore index 8b1ed09a..e9e6c0d4 100644 --- a/TnT/.gitignore +++ b/TnT/.gitignore @@ -78,6 +78,6 @@ master.key # PList # => 전체 plist를 다 제외함. -# **/*.plist -# Support/*.plist +**/*.plist +Resources/GoogleService-Info.plist GoogleService-Info.plist \ No newline at end of file From 3a30bd866b62fd3dc578936d83194516d4163729 Mon Sep 17 00:00:00 2001 From: Park Seo Yeon Date: Fri, 31 Jan 2025 16:45:18 +0900 Subject: [PATCH 3/9] =?UTF-8?q?[Feat]=20UseCase,=20UseCaseRepo=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC=20=ED=9B=84=20=EC=86=8C=EC=85=9C=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 테스트전 --- .../DIContainer/Sources/DIContainer.swift | 16 ++- .../SocialLogInRepositoryImpl.swift | 39 +++--- .../Service/User/UserRepositoryImpl.swift | 36 ------ .../Sources/Entity/SocailLoginEntity.swift | 119 ++++++++++++++++++ .../Sources/Mapper/PostSocialMapper.swift | 31 +++++ .../Sources/UseCase/SocialLoginUseCase.swift | 30 +++++ .../Domain/Sources/UseCase/UserUseCase.swift | 21 +++- .../Onboarding/Common/OnboardingFeature.swift | 67 +++++++++- TnT/Tuist/Package.resolved | 117 +++++++++++++++++ TnT/Tuist/Package.swift | 3 +- .../Dependency/DependencyInformation.swift | 3 +- .../Dependency/ExternalDependency.swift | 3 +- 12 files changed, 418 insertions(+), 67 deletions(-) create mode 100644 TnT/Projects/Domain/Sources/Entity/SocailLoginEntity.swift create mode 100644 TnT/Projects/Domain/Sources/Mapper/PostSocialMapper.swift create mode 100644 TnT/Projects/Domain/Sources/UseCase/SocialLoginUseCase.swift diff --git a/TnT/Projects/DIContainer/Sources/DIContainer.swift b/TnT/Projects/DIContainer/Sources/DIContainer.swift index 6fb9b504..3f2b327f 100644 --- a/TnT/Projects/DIContainer/Sources/DIContainer.swift +++ b/TnT/Projects/DIContainer/Sources/DIContainer.swift @@ -12,7 +12,12 @@ import Data // MARK: - Swift-Dependencies private enum UserUseCaseKey: DependencyKey { - static let liveValue: UserUseCase = DefaultUserUseCase(userRepository: UserRepositoryImpl()) +// static let liveValue: UserUseCase + static let liveValue: UserUseCase = DefaultUserUseCase(userRepostiory: UserRepositoryImpl()) +} + +private enum UserUseCaseRepoKey: DependencyKey { + static let liveValue: UserRepository = DefaultUserUseCase(userRepostiory: UserRepositoryImpl()) } private enum TraineeUseCaseKey: DependencyKey { @@ -20,7 +25,7 @@ private enum TraineeUseCaseKey: DependencyKey { } private enum SocialUseCaseKey: DependencyKey { - static let liveValue: SocialLoginUserCase = DefaultSocialLoginUserCase(socialLoginRepository: SocialLogInRepositoryImpl()) + static let liveValue: SocialLoginUseCase = SocialLoginUseCase(socialLoginRepository: SocialLogInRepositoryImpl(loginManager: SNSLoginManager())) } // MARK: - DependencyValues @@ -30,12 +35,17 @@ public extension DependencyValues { set { self[UserUseCaseKey.self] = newValue } } + var userUseRepoCase: UserRepository { + get { self[UserUseCaseRepoKey.self] } + set { self[UserUseCaseRepoKey.self] = newValue } + } + var traineeUseCase: TraineeUseCase { get { self[TraineeUseCaseKey.self] } set { self[TraineeUseCaseKey.self] = newValue } } - var socialLogInUseCase: SocialLoginUserCase { + var socialLogInUseCase: SocialLoginUseCase { get { self[SocialUseCaseKey.self] } set { self[SocialUseCaseKey.self] = newValue } } diff --git a/TnT/Projects/Data/Sources/Network/Service/SocialLogin/SocialLogInRepositoryImpl.swift b/TnT/Projects/Data/Sources/Network/Service/SocialLogin/SocialLogInRepositoryImpl.swift index 0e0295c1..009f07c1 100644 --- a/TnT/Projects/Data/Sources/Network/Service/SocialLogin/SocialLogInRepositoryImpl.swift +++ b/TnT/Projects/Data/Sources/Network/Service/SocialLogin/SocialLogInRepositoryImpl.swift @@ -19,12 +19,29 @@ public enum LoginError: Error { case unknown(message: String) } -//public struct SocialLogInRepositoryImpl: UserRepository { -// -// public let loginManager = SNSLoginManager() -// -// public init() {} -// +public struct SocialLogInRepositoryImpl: SocialLoginRepository { + + public let loginManager: SNSLoginManager + + public init(loginManager: SNSLoginManager) { + self.loginManager = loginManager + } + + public func appleLogin() async -> AppleLoginInfo? { + let result = await loginManager.appleLogin() + + return result + } + + public func kakaoLogin() async -> KakaoLoginInfo? { + let result = await loginManager.kakaoLogin() + return result + } + + public func kakaoLogout() async { + // 미구현 + } + // public func appleLoginResult() async throws -> Result { // let result = await loginManager.appleLogin() // @@ -60,12 +77,4 @@ public enum LoginError: Error { // // return .success(kakaoRequest) // } -// -// public func postSocialLogin(_ reqDTO: Domain.PostSocialLoginReqDTO) async throws -> PostSocialLoginResDTO { -// -// } -// -// public func postSignUp(_ reqDTO: Domain.PostSignUpReqDTO, profileImage: Data?) async throws -> PostSignUpResDTO { -// <#code#> -// } -//} +} diff --git a/TnT/Projects/Data/Sources/Network/Service/User/UserRepositoryImpl.swift b/TnT/Projects/Data/Sources/Network/Service/User/UserRepositoryImpl.swift index 23746e17..d25e473e 100644 --- a/TnT/Projects/Data/Sources/Network/Service/User/UserRepositoryImpl.swift +++ b/TnT/Projects/Data/Sources/Network/Service/User/UserRepositoryImpl.swift @@ -36,40 +36,4 @@ public struct UserRepositoryImpl: UserRepository { decodingType: PostSignUpResDTO.self ) } - - public func appleLoginResult() async throws -> Result { - let result = await loginManager.appleLogin() - - guard let result else { - return .failure(LoginError.appleError) - } - - let appleRequest = PostSocialLoginReqDTO( - socialType: "APPLE", - fcmToken: "", - socialAccessToken: "", - authorizationCode: result.authorizationCode, - idToken: result.identityToken - ) - - return .success(appleRequest) - } - - public func kakaoLoginResult() async throws -> Result { - let result = await loginManager.kakaoLogin() - - guard let result else { - return .failure(LoginError.kakaoError) - } - - let kakaoRequest = PostSocialLoginReqDTO( - socialType: "KAKAO", - fcmToken: "", - socialAccessToken: result.accessToken, - authorizationCode: "", - idToken: "" - ) - - return .success(kakaoRequest) - } } diff --git a/TnT/Projects/Domain/Sources/Entity/SocailLoginEntity.swift b/TnT/Projects/Domain/Sources/Entity/SocailLoginEntity.swift new file mode 100644 index 00000000..732aa9df --- /dev/null +++ b/TnT/Projects/Domain/Sources/Entity/SocailLoginEntity.swift @@ -0,0 +1,119 @@ +// +// PostSocailEntity.swift +// Domain +// +// Created by 박서연 on 1/31/25. +// Copyright © 2025 yapp25thTeamTnT. All rights reserved. +// + +import Foundation + +/// 소셜 로그인 요청 DTO +public struct PostSocailEntity: Equatable { + /// 소셜 로그인 타입 (KAKAO, APPLE) + let socialType: String + /// FCM 토큰 + let fcmToken: String + /// 소셜 액세스 토큰 + let socialAccessToken: String? + /// 애플 인가 코드 (Apple 로그인 시 필요) + let authorizationCode: String? + /// 애플 ID 토큰 (Apple 로그인 시 필요) + let idToken: String? + + public init( + socialType: String, + fcmToken: String, + socialAccessToken: String?, + authorizationCode: String?, + idToken: String? + ) { + self.socialType = socialType + self.fcmToken = fcmToken + self.socialAccessToken = socialAccessToken + self.authorizationCode = authorizationCode + self.idToken = idToken + } +} + +/// 소셜 로그인 응답 DTO +public struct PostSocialLoginResEntity: Equatable { + /// 세션 ID + public let sessionId: String? + /// 소셜 로그인 ID + public let socialId: String + /// 소셜 이메일 + public let socialEmail: String + /// 소셜 로그인 타입 (KAKAO, APPLE) + public let socialType: String + /// 가입 여부 (`true`: 이미 가입됨, `false`: 미가입) + public let isSignUp: Bool +} + +/// 회원가입 요청 DTO +public struct PostSignUpReqEntity { + /// FCM 토큰 + let fcmToken: String + /// 회원 타입 (trainer, trainee) + let memberType: String + /// 소셜 로그인 타입 (KAKAO, APPLE) + let socialType: String + /// 소셜 로그인 ID + let socialId: String + /// 소셜 로그인 이메일 + let socialEmail: String + /// 서비스 이용 약관 동의 여부 + let serviceAgreement: Bool + /// 개인정보 수집 동의 여부 + let collectionAgreement: Bool + /// 광고성 알림 수신 동의 여부 + let advertisementAgreement: Bool + /// 푸시 알림 수신 동의 여부 + let pushAgreement: Bool + /// 회원 이름 + let name: String + /// 생년월일 (yyyy-MM-dd) + let birthday: String? + /// 키 (cm) + let height: Double? + /// 몸무게 (kg, 소수점 1자리까지 가능) + let weight: Double? + /// 트레이너에게 전달할 주의사항 + let cautionNote: String? + /// PT 목적 (체중 감량, 근력 향상 등) + let goalContents: [String]? + + public init( + fcmToken: String, + memberType: String, + socialType: String, + socialId: String, + socialEmail: String, + serviceAgreement: Bool, + collectionAgreement: Bool, + advertisementAgreement: Bool, + pushAgreement: Bool, + name: String, + birthday: String?, + height: Double?, + weight: Double?, + cautionNote: String?, + goalContents: [String]? + ) { + self.fcmToken = fcmToken + self.memberType = memberType + self.socialType = socialType + self.socialId = socialId + self.socialEmail = socialEmail + self.serviceAgreement = serviceAgreement + self.collectionAgreement = collectionAgreement + self.advertisementAgreement = advertisementAgreement + self.pushAgreement = pushAgreement + self.name = name + self.birthday = birthday + self.height = height + self.weight = weight + self.cautionNote = cautionNote + self.goalContents = goalContents + } +} diff --git a/TnT/Projects/Domain/Sources/Mapper/PostSocialMapper.swift b/TnT/Projects/Domain/Sources/Mapper/PostSocialMapper.swift new file mode 100644 index 00000000..49310037 --- /dev/null +++ b/TnT/Projects/Domain/Sources/Mapper/PostSocialMapper.swift @@ -0,0 +1,31 @@ +// +// PostSocialMapper.swift +// Domain +// +// Created by 박서연 on 1/31/25. +// Copyright © 2025 yapp25thTeamTnT. All rights reserved. +// + +import Foundation + +public struct PostSocialMapper { + public static func toDTO(from entity: PostSocailEntity) -> PostSocialLoginReqDTO { + return PostSocialLoginReqDTO( + socialType: entity.socialType, + fcmToken: entity.fcmToken, + socialAccessToken: entity.socialAccessToken, + authorizationCode: entity.authorizationCode, + idToken: entity.idToken + ) + } + + public static func toEntity(from dto: PostSocialLoginReqDTO) -> PostSocailEntity { + return PostSocailEntity( + socialType: dto.socialType, + fcmToken: dto.fcmToken, + socialAccessToken: dto.socialAccessToken, + authorizationCode: dto.authorizationCode, + idToken: dto.idToken + ) + } +} diff --git a/TnT/Projects/Domain/Sources/UseCase/SocialLoginUseCase.swift b/TnT/Projects/Domain/Sources/UseCase/SocialLoginUseCase.swift new file mode 100644 index 00000000..17f2ab67 --- /dev/null +++ b/TnT/Projects/Domain/Sources/UseCase/SocialLoginUseCase.swift @@ -0,0 +1,30 @@ +// +// SocialLoginUseCase.swift +// Domain +// +// Created by 박서연 on 1/29/25. +// Copyright © 2025 yapp25thTeamTnT. All rights reserved. +// + +import Foundation + +public struct SocialLoginUseCase { + + private let socialLoginRepository: SocialLoginRepository + + public init(socialLoginRepository: SocialLoginRepository) { + self.socialLoginRepository = socialLoginRepository + } + + public func appleLogin() async -> AppleLoginInfo? { + return await socialLoginRepository.appleLogin() + } + + public func kakaoLogin() async -> KakaoLoginInfo? { + return await socialLoginRepository.kakaoLogin() + } + + public func kakaoLogout() async { + // 미구현 + } +} diff --git a/TnT/Projects/Domain/Sources/UseCase/UserUseCase.swift b/TnT/Projects/Domain/Sources/UseCase/UserUseCase.swift index 852f9772..b4db8f42 100644 --- a/TnT/Projects/Domain/Sources/UseCase/UserUseCase.swift +++ b/TnT/Projects/Domain/Sources/UseCase/UserUseCase.swift @@ -7,6 +7,7 @@ // import Dependencies +import SwiftUI // MARK: - UserUseCase 프로토콜 public protocol UserUseCase { @@ -27,13 +28,15 @@ public protocol UserUseCase { } // MARK: - Default 구현체 -public struct DefaultUserUseCase: UserUseCase { - private let userRepository: UserRepository +public struct DefaultUserUseCase: UserRepository, UserUseCase { - public init(userRepository: UserRepository) { - self.userRepository = userRepository + public let userRepostiory: UserRepository + + public init(userRepostiory: UserRepository) { + self.userRepostiory = userRepostiory } + // MARK: - Usecase public func validateUserName(_ name: String) -> Bool { return !name.isEmpty && UserPolicy.userNameInput.textValidation(name) } @@ -61,4 +64,14 @@ public struct DefaultUserUseCase: UserUseCase { public func getPrecautionMaxLength() -> Int { return UserPolicy.maxPrecautionLength } + + // MARK: - Repository + public func postSocialLogin(_ reqDTO: PostSocialLoginReqDTO) async throws -> PostSocialLoginResDTO { + return try await userRepostiory.postSocialLogin(reqDTO) + } + + public func postSignUp(_ reqDTO: PostSignUpReqDTO, profileImage: Data?) async throws -> PostSignUpResDTO { + return try await userRepostiory.postSignUp(reqDTO, profileImage: profileImage) + } + } diff --git a/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingFeature.swift b/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingFeature.swift index 1294e622..8753e0e1 100644 --- a/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingFeature.swift +++ b/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingFeature.swift @@ -22,12 +22,17 @@ public struct OnboardingFeature { public var socialType: LoginType? public var termAgree: Bool = true public var socialEmail: String? = "" + public var postUserEntity: PostSocailEntity? public init(path: StackState = StackState()) { self.path = path } } + @Dependency(\.userUseCase) private var userUseCase: UserUseCase + @Dependency(\.userUseRepoCase) private var userUseCaseRepo: UserRepository + @Dependency(\.socialLogInUseCase) private var socialLoginUseCase: SocialLoginUseCase + public enum Action: ViewAction { /// 뷰에서 일어나는 액션을 처리합니다.(카카오,애플로그인 실행) case view(View) @@ -40,6 +45,9 @@ public struct OnboardingFeature { public enum View: Equatable { case tappedAppleLogin case tappedKakaoLogin + case postSocialLogin(entity: PostSocailEntity) + case postSignUp + case socailLoginFail } @CasePathable @@ -64,6 +72,8 @@ public struct OnboardingFeature { case toRegisterInvitationCode /// 트레이니의 pt 횟수 및 정보 입력화면으로 이동 case toRegisterPtClassInfo + /// 홈으로 이동 + case toHome } } @@ -75,10 +85,53 @@ public struct OnboardingFeature { case let .view(view): switch view { case .tappedAppleLogin: - state.path.append(.term(TermFeature.State())) - return .none + return .run { @Sendable send in + let result = await socialLoginUseCase.appleLogin() + guard let result else { return } + let entity = PostSocailEntity( + socialType: "APPLE", + fcmToken: "", + socialAccessToken: "", + authorizationCode: result.authorizationCode, + idToken: result.identityToken + ) + + await send(.view(.postSocialLogin(entity: entity))) + } + case .tappedKakaoLogin: - state.path.append(.term(TermFeature.State())) + return .run { @Sendable send in + let result = await socialLoginUseCase.kakaoLogin() + guard let result else { return } + + let entity = PostSocailEntity( + socialType: "KAKAO", + fcmToken: "", + socialAccessToken: result.accessToken, + authorizationCode: "", + idToken: "" + ) + + await send(.view(.postSocialLogin(entity: entity))) + } + + case .postSocialLogin: + guard let postEntity = state.postUserEntity else { return .none } + let entity = PostSocialMapper.toDTO(from: postEntity) + + return .run { send in + do { + let result = try await userUseCaseRepo.postSocialLogin(entity) + await send(.move(.toHome)) + } catch { + await send(.move(.toTermview)) + } + } + + case .socailLoginFail: + return .none + + case .postSignUp: return .none } @@ -99,6 +152,9 @@ public struct OnboardingFeature { case .toMakeInvitationCode: state.path.append(.makeInvitationCode(MakeInvitationCodeFeature.State())) return .none + case .toHome: + print("post 사인 성공..") + return .none default: return .none } @@ -123,9 +179,6 @@ public struct OnboardingFeature { default: return .none } - - default: - return .none } } .forEach(\.path, action: \.path) @@ -153,5 +206,7 @@ public struct OnboardingFeature { case registerInvitationCode /// 트레이니 수업 정보 입력 case registerPtClassInfo + /// 홈 + case home } } diff --git a/TnT/Tuist/Package.resolved b/TnT/Tuist/Package.resolved index 14c7aa10..77d93e06 100644 --- a/TnT/Tuist/Package.resolved +++ b/TnT/Tuist/Package.resolved @@ -1,5 +1,14 @@ { "pins" : [ + { + "identity" : "abseil-cpp-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/abseil-cpp-binary.git", + "state" : { + "revision" : "194a6706acbd25e4ef639bcaddea16e8758a3e27", + "version" : "1.2024011602.0" + } + }, { "identity" : "alamofire", "kind" : "remoteSourceControl", @@ -9,6 +18,15 @@ "version" : "5.10.2" } }, + { + "identity" : "app-check", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/app-check.git", + "state" : { + "revision" : "61b85103a1aeed8218f17c794687781505fbbef5", + "version" : "11.2.0" + } + }, { "identity" : "combine-schedulers", "kind" : "remoteSourceControl", @@ -18,6 +36,69 @@ "version" : "1.0.3" } }, + { + "identity" : "firebase-ios-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/firebase-ios-sdk", + "state" : { + "revision" : "0d885d28250fb1196b614bc9455079b75c531f72", + "version" : "11.7.0" + } + }, + { + "identity" : "googleappmeasurement", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleAppMeasurement.git", + "state" : { + "revision" : "be0881ff728eca210ccb628092af400c086abda3", + "version" : "11.7.0" + } + }, + { + "identity" : "googledatatransport", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleDataTransport.git", + "state" : { + "revision" : "617af071af9aa1d6a091d59a202910ac482128f9", + "version" : "10.1.0" + } + }, + { + "identity" : "googleutilities", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleUtilities.git", + "state" : { + "revision" : "53156c7ec267db846e6b64c9f4c4e31ba4cf75eb", + "version" : "8.0.2" + } + }, + { + "identity" : "grpc-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/grpc-binary.git", + "state" : { + "revision" : "f56d8fc3162de9a498377c7b6cea43431f4f5083", + "version" : "1.65.1" + } + }, + { + "identity" : "gtm-session-fetcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/gtm-session-fetcher.git", + "state" : { + "revision" : "3cdb78efb79b4a5383c3911488d8025bfc545b5e", + "version" : "4.3.0" + } + }, + { + "identity" : "interop-ios-for-google-sdks", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/interop-ios-for-google-sdks.git", + "state" : { + "revision" : "2d12673670417654f08f5f90fdd62926dc3a2648", + "version" : "100.0.0" + } + }, { "identity" : "kakao-ios-sdk", "kind" : "remoteSourceControl", @@ -27,6 +108,15 @@ "version" : "2.23.0" } }, + { + "identity" : "leveldb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/leveldb.git", + "state" : { + "revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1", + "version" : "1.22.5" + } + }, { "identity" : "lottie-ios", "kind" : "remoteSourceControl", @@ -36,6 +126,24 @@ "version" : "4.5.1" } }, + { + "identity" : "nanopb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/nanopb.git", + "state" : { + "revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1", + "version" : "2.30910.0" + } + }, + { + "identity" : "promises", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/promises.git", + "state" : { + "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", + "version" : "2.4.0" + } + }, { "identity" : "swift-case-paths", "kind" : "remoteSourceControl", @@ -126,6 +234,15 @@ "version" : "1.4.1" } }, + { + "identity" : "swift-protobuf", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-protobuf.git", + "state" : { + "revision" : "ebc7251dd5b37f627c93698e4374084d98409633", + "version" : "1.28.2" + } + }, { "identity" : "swift-sharing", "kind" : "remoteSourceControl", diff --git a/TnT/Tuist/Package.swift b/TnT/Tuist/Package.swift index 492314c3..f7416003 100644 --- a/TnT/Tuist/Package.swift +++ b/TnT/Tuist/Package.swift @@ -17,6 +17,7 @@ let package = Package( .package(url: "https://github.com/pointfreeco/swift-composable-architecture", from: "1.17.0"), .package(url: "https://github.com/airbnb/lottie-ios", from: "4.5.0"), .package(url: "https://github.com/kakao/kakao-ios-sdk.git", from: "2.20.0"), - .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.6.3") + .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.6.3"), + .package(url: "https://github.com/firebase/firebase-ios-sdk", from: "11.7.0") ] ) diff --git a/TnT/Tuist/ProjectDescriptionHelpers/Dependency/DependencyInformation.swift b/TnT/Tuist/ProjectDescriptionHelpers/Dependency/DependencyInformation.swift index 7da5ee1c..607b6e10 100644 --- a/TnT/Tuist/ProjectDescriptionHelpers/Dependency/DependencyInformation.swift +++ b/TnT/Tuist/ProjectDescriptionHelpers/Dependency/DependencyInformation.swift @@ -11,7 +11,7 @@ let dependencyInfo: [DependencyInformation: [DependencyInformation]] = [ .TnTApp: [.Presentation, .Data], .Presentation: [.DIContainer, .DesignSystem, .Domain, .ComposableArchitecture], .Domain: [.SwiftDepedencies], - .Data: [.Domain, .KakaoSDKUser, .SwiftDepedencies], + .Data: [.Domain, .KakaoSDKUser, .SwiftDepedencies, .FirebaseMessaging], .DesignSystem: [.Lottie], .DIContainer: [.Domain, .Data] ] @@ -27,6 +27,7 @@ public enum DependencyInformation: String, CaseIterable, Sendable { case ComposableArchitecture = "ComposableArchitecture" case KakaoSDKUser = "KakaoSDKUser" case SwiftDepedencies = "Dependencies" + case FirebaseMessaging = "FirebaseMessaging" } public extension DependencyInformation { diff --git a/TnT/Tuist/ProjectDescriptionHelpers/Dependency/ExternalDependency.swift b/TnT/Tuist/ProjectDescriptionHelpers/Dependency/ExternalDependency.swift index 32da25b8..622ff1d6 100644 --- a/TnT/Tuist/ProjectDescriptionHelpers/Dependency/ExternalDependency.swift +++ b/TnT/Tuist/ProjectDescriptionHelpers/Dependency/ExternalDependency.swift @@ -11,5 +11,6 @@ let externalDependency: [DependencyInformation] = [ .KakaoSDKUser, .Lottie, .ComposableArchitecture, - .SwiftDepedencies + .SwiftDepedencies, + .FirebaseMessaging ] From 23e8c0ebd4874762f94203a14aa9a77587ec2a60 Mon Sep 17 00:00:00 2001 From: Park Seo Yeon Date: Sat, 1 Feb 2025 03:13:56 +0900 Subject: [PATCH 4/9] [Feat] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 소셜 로그인 postSingIn까지 구현 완료 - 추후 데이터 연결을 어찌할지? --- .../Sources/Mapper/PostSocialMapper.swift | 22 +++++++++++++++++++ .../Onboarding/Common/OnboardingFeature.swift | 9 ++++---- .../Onboarding/Common/OnboardingView.swift | 6 ++--- .../Onboarding/Common/TermFeature.swift | 9 ++++++-- .../Sources/Onboarding/Common/TermView.swift | 6 ++--- TnT/Projects/TnTApp/Sources/ContentView.swift | 5 +++-- TnT/Projects/TnTApp/Sources/TnTApp.swift | 7 +++++- TnT/Projects/TnTApp/TnTApp.entitlements | 10 +++++++++ .../Project/Target+Templates.swift | 1 + 9 files changed, 58 insertions(+), 17 deletions(-) create mode 100644 TnT/Projects/TnTApp/TnTApp.entitlements diff --git a/TnT/Projects/Domain/Sources/Mapper/PostSocialMapper.swift b/TnT/Projects/Domain/Sources/Mapper/PostSocialMapper.swift index 49310037..c299a03a 100644 --- a/TnT/Projects/Domain/Sources/Mapper/PostSocialMapper.swift +++ b/TnT/Projects/Domain/Sources/Mapper/PostSocialMapper.swift @@ -28,4 +28,26 @@ public struct PostSocialMapper { idToken: dto.idToken ) } + + /// `PostSocialLoginResDTO` → `PostSocialLoginResEntity` 변환 + public static func toResEntity(from dto: PostSocialLoginResDTO) -> PostSocialLoginResEntity { + return PostSocialLoginResEntity( + sessionId: dto.sessionId, + socialId: dto.socialId, + socialEmail: dto.socialEmail, + socialType: dto.socialType, + isSignUp: dto.isSignUp + ) + } + + /// `PostSocialLoginResEntity` → `PostSocialLoginResDTO` 변환 + public static func toDTO(from entity: PostSocialLoginResEntity) -> PostSocialLoginResDTO { + return PostSocialLoginResDTO( + sessionId: entity.sessionId, + socialId: entity.socialId, + socialEmail: entity.socialEmail, + socialType: entity.socialType, + isSignUp: entity.isSignUp + ) + } } diff --git a/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingFeature.swift b/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingFeature.swift index 8753e0e1..637777d3 100644 --- a/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingFeature.swift +++ b/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingFeature.swift @@ -115,13 +115,11 @@ public struct OnboardingFeature { await send(.view(.postSocialLogin(entity: entity))) } - case .postSocialLogin: - guard let postEntity = state.postUserEntity else { return .none } - let entity = PostSocialMapper.toDTO(from: postEntity) - + case .postSocialLogin(let entity): + let post = PostSocialMapper.toDTO(from: entity) return .run { send in do { - let result = try await userUseCaseRepo.postSocialLogin(entity) + let result = try await userUseCaseRepo.postSocialLogin(post) await send(.move(.toHome)) } catch { await send(.move(.toTermview)) @@ -140,6 +138,7 @@ public struct OnboardingFeature { case .toTermview: state.path.append(.term(TermFeature.State())) return .none + case .toselectRole: state.path.append(.selectRole) return .none diff --git a/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingView.swift b/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingView.swift index d1b7693f..9bd2ca0e 100644 --- a/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingView.swift +++ b/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingView.swift @@ -84,10 +84,8 @@ public struct OnboardingView: View { .background(type.background) .clipShape(RoundedRectangle(cornerRadius: 12)) .onTapGesture { - store.send(.move(.toTermview)) -// type == .kakao -// ? store.send(.view(.tappedAppleLogin)) -// : store.send(.view(.tappedAppleLogin)) + type == .kakao ? store.send(.view(.tappedKakaoLogin)) + : store.send(.view(.tappedAppleLogin)) } } } diff --git a/TnT/Projects/Presentation/Sources/Onboarding/Common/TermFeature.swift b/TnT/Projects/Presentation/Sources/Onboarding/Common/TermFeature.swift index 8c4e8ae3..47029d8c 100644 --- a/TnT/Projects/Presentation/Sources/Onboarding/Common/TermFeature.swift +++ b/TnT/Projects/Presentation/Sources/Onboarding/Common/TermFeature.swift @@ -13,13 +13,18 @@ import ComposableArchitecture public struct TermFeature { @ObservableState public struct State: Equatable { - var view_terms: [Term: Bool] = [:] + var view_terms: [Term: Bool] var view_isAllAgreed: Bool { view_terms.values.allSatisfy { $0 } } var isNavigaiton: Bool = false - public init() { } + public init() { + self.view_terms = [ + .term: false, + .personalInfo: false + ] + } } public enum Action: Equatable, ViewAction { diff --git a/TnT/Projects/Presentation/Sources/Onboarding/Common/TermView.swift b/TnT/Projects/Presentation/Sources/Onboarding/Common/TermView.swift index 23a1a313..ea24ce83 100644 --- a/TnT/Projects/Presentation/Sources/Onboarding/Common/TermView.swift +++ b/TnT/Projects/Presentation/Sources/Onboarding/Common/TermView.swift @@ -12,7 +12,7 @@ import ComposableArchitecture import DesignSystem @ViewAction(for: TermFeature.self) -struct TermView: View { +public struct TermView: View { public let store: StoreOf @@ -20,7 +20,7 @@ struct TermView: View { self.store = store } - var body: some View { + public var body: some View { VStack { VStack(alignment: .leading, spacing: 0) { Header() @@ -80,7 +80,7 @@ struct TermView: View { term: term, isAgreed: store.view_terms[term] ?? false ) { - send(.toggleTerm(term, $0)) + send(.toggleTerm(term, !$0)) } } } diff --git a/TnT/Projects/TnTApp/Sources/ContentView.swift b/TnT/Projects/TnTApp/Sources/ContentView.swift index 2bf81b08..dd422ce6 100644 --- a/TnT/Projects/TnTApp/Sources/ContentView.swift +++ b/TnT/Projects/TnTApp/Sources/ContentView.swift @@ -12,8 +12,9 @@ import ComposableArchitecture struct ContentView: View { var body: some View { - Text("dasdasdf") - Text("Hello, World!") + TermView(store: Store(initialState: TermFeature.State(), reducer: { + TermFeature() + })) } } diff --git a/TnT/Projects/TnTApp/Sources/TnTApp.swift b/TnT/Projects/TnTApp/Sources/TnTApp.swift index ca9ab0fc..4f0825b3 100644 --- a/TnT/Projects/TnTApp/Sources/TnTApp.swift +++ b/TnT/Projects/TnTApp/Sources/TnTApp.swift @@ -8,6 +8,8 @@ import SwiftUI +import Presentation +import ComposableArchitecture import DesignSystem @main @@ -19,7 +21,10 @@ struct ToyProjectApp: App { var body: some Scene { WindowGroup { - ContentView() + OnboardingView(store: Store(initialState: OnboardingFeature.State(), reducer: { + OnboardingFeature() + })) +// ContentView() } } } diff --git a/TnT/Projects/TnTApp/TnTApp.entitlements b/TnT/Projects/TnTApp/TnTApp.entitlements new file mode 100644 index 00000000..a812db50 --- /dev/null +++ b/TnT/Projects/TnTApp/TnTApp.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.developer.applesignin + + Default + + + diff --git a/TnT/Tuist/ProjectDescriptionHelpers/Project/Target+Templates.swift b/TnT/Tuist/ProjectDescriptionHelpers/Project/Target+Templates.swift index 4be59b37..b2629b78 100644 --- a/TnT/Tuist/ProjectDescriptionHelpers/Project/Target+Templates.swift +++ b/TnT/Tuist/ProjectDescriptionHelpers/Project/Target+Templates.swift @@ -68,6 +68,7 @@ public extension Target { infoPlist: .file(path: .relativeToRoot("Tuist/Config/Info.plist")), sources: ["Sources/**"], resources: ["Resources/**"], + entitlements: "\(appName).entitlements", scripts: [.swiftLint], dependencies: dependencies ) From 84453feef1f79b74f4822652a0a272c570e806bc Mon Sep 17 00:00:00 2001 From: Park Seo Yeon Date: Wed, 29 Jan 2025 05:12:16 +0900 Subject: [PATCH 5/9] =?UTF-8?q?[Feat]=20userUseCase=EC=97=90=20=EC=86=8C?= =?UTF-8?q?=EC=85=9C=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DIContainer/Sources/DIContainer.swift | 9 +++ .../SocialLogInRepositoryImpl.swift | 71 +++++++++++++++++++ .../Service/User/UserRepositoryImpl.swift | 38 +++++++++- .../Common/OnboardingFeature.swift | 8 +++ .../Common/OnboardingView.swift | 0 .../Common/TermFeature.swift | 0 .../Common/TermView.swift | 0 .../Code/MakeInvitationCodeFeature.swift | 0 .../Login/Code/MakeInvitationCodeView.swift | 0 .../ConnectedTraineeProfileFeature.swift | 0 .../ConnectedTraineeProfileView.swift | 0 .../ConnectionCompleteFeature.swift | 0 .../Connection/ConnectionCompleteView.swift | 0 .../TrainerSignUpCompleteFeature.swift | 0 .../Profile/TrainerSignUpCompleteView.swift | 0 15 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 TnT/Projects/Data/Sources/Network/Service/SocialLogin/SocialLogInRepositoryImpl.swift rename TnT/Projects/Presentation/Sources/{Onbarding => Onboarding}/Common/OnboardingFeature.swift (95%) rename TnT/Projects/Presentation/Sources/{Onbarding => Onboarding}/Common/OnboardingView.swift (100%) rename TnT/Projects/Presentation/Sources/{Onbarding => Onboarding}/Common/TermFeature.swift (100%) rename TnT/Projects/Presentation/Sources/{Onbarding => Onboarding}/Common/TermView.swift (100%) rename TnT/Projects/Presentation/Sources/{Onbarding => Onboarding}/Trainer/Login/Code/MakeInvitationCodeFeature.swift (100%) rename TnT/Projects/Presentation/Sources/{Onbarding => Onboarding}/Trainer/Login/Code/MakeInvitationCodeView.swift (100%) rename TnT/Projects/Presentation/Sources/{Onbarding => Onboarding}/Trainer/Login/Connection/ConnectedTraineeProfileFeature.swift (100%) rename TnT/Projects/Presentation/Sources/{Onbarding => Onboarding}/Trainer/Login/Connection/ConnectedTraineeProfileView.swift (100%) rename TnT/Projects/Presentation/Sources/{Onbarding => Onboarding}/Trainer/Login/Connection/ConnectionCompleteFeature.swift (100%) rename TnT/Projects/Presentation/Sources/{Onbarding => Onboarding}/Trainer/Login/Connection/ConnectionCompleteView.swift (100%) rename TnT/Projects/Presentation/Sources/{Onbarding => Onboarding}/Trainer/Login/Profile/TrainerSignUpCompleteFeature.swift (100%) rename TnT/Projects/Presentation/Sources/{Onbarding => Onboarding}/Trainer/Login/Profile/TrainerSignUpCompleteView.swift (100%) diff --git a/TnT/Projects/DIContainer/Sources/DIContainer.swift b/TnT/Projects/DIContainer/Sources/DIContainer.swift index 4ad44f59..4f7a83dc 100644 --- a/TnT/Projects/DIContainer/Sources/DIContainer.swift +++ b/TnT/Projects/DIContainer/Sources/DIContainer.swift @@ -22,6 +22,10 @@ private enum TraineeUseCaseKey: DependencyKey { ) } +private enum SocialUseCaseKey: DependencyKey { + static let liveValue: SocialLoginUserCase = DefaultSocialLoginUserCase(socialLoginRepository: SocialLogInRepositoryImpl()) +} + // MARK: - DependencyValues public extension DependencyValues { var userUseCase: UserUseCase { @@ -33,4 +37,9 @@ public extension DependencyValues { get { self[TraineeUseCaseKey.self] } set { self[TraineeUseCaseKey.self] = newValue } } + + var socialLogInUseCase: SocialLoginUserCase { + get { self[SocialUseCaseKey.self] } + set { self[SocialUseCaseKey.self] = newValue } + } } diff --git a/TnT/Projects/Data/Sources/Network/Service/SocialLogin/SocialLogInRepositoryImpl.swift b/TnT/Projects/Data/Sources/Network/Service/SocialLogin/SocialLogInRepositoryImpl.swift new file mode 100644 index 00000000..0e0295c1 --- /dev/null +++ b/TnT/Projects/Data/Sources/Network/Service/SocialLogin/SocialLogInRepositoryImpl.swift @@ -0,0 +1,71 @@ +// +// SocialLogInRepositoryImpl.swift +// Data +// +// Created by 박서연 on 1/29/25. +// Copyright © 2025 yapp25thTeamTnT. All rights reserved. +// + +import Foundation +import Dependencies + +import Domain + +public enum LoginError: Error { + case invalidCredentials + case networkFailure + case kakaoError + case appleError + case unknown(message: String) +} + +//public struct SocialLogInRepositoryImpl: UserRepository { +// +// public let loginManager = SNSLoginManager() +// +// public init() {} +// +// public func appleLoginResult() async throws -> Result { +// let result = await loginManager.appleLogin() +// +// guard let result else { +// return .failure(LoginError.appleError) +// } +// +// let appleRequest = PostSocialLoginReqDTO( +// socialType: "APPLE", +// fcmToken: "", +// socialAccessToken: "", +// authorizationCode: result.authorizationCode, +// idToken: result.identityToken +// ) +// +// return .success(appleRequest) +// } +// +// public func kakaoLoginResult() async throws -> Result { +// let result = await loginManager.kakaoLogin() +// +// guard let result else { +// return .failure(LoginError.kakaoError) +// } +// +// let kakaoRequest = PostSocialLoginReqDTO( +// socialType: "KAKAO", +// fcmToken: "", +// socialAccessToken: result.accessToken, +// authorizationCode: "", +// idToken: "" +// ) +// +// return .success(kakaoRequest) +// } +// +// public func postSocialLogin(_ reqDTO: Domain.PostSocialLoginReqDTO) async throws -> PostSocialLoginResDTO { +// +// } +// +// public func postSignUp(_ reqDTO: Domain.PostSignUpReqDTO, profileImage: Data?) async throws -> PostSignUpResDTO { +// <#code#> +// } +//} diff --git a/TnT/Projects/Data/Sources/Network/Service/User/UserRepositoryImpl.swift b/TnT/Projects/Data/Sources/Network/Service/User/UserRepositoryImpl.swift index 34ef8b28..23746e17 100644 --- a/TnT/Projects/Data/Sources/Network/Service/User/UserRepositoryImpl.swift +++ b/TnT/Projects/Data/Sources/Network/Service/User/UserRepositoryImpl.swift @@ -13,7 +13,7 @@ import Domain /// 사용자 관련 네트워크 요청을 처리하는 UserRepository 구현체 public struct UserRepositoryImpl: UserRepository { - + private let loginManager = SNSLoginManager() private let networkService: NetworkService = .shared public init() {} @@ -36,4 +36,40 @@ public struct UserRepositoryImpl: UserRepository { decodingType: PostSignUpResDTO.self ) } + + public func appleLoginResult() async throws -> Result { + let result = await loginManager.appleLogin() + + guard let result else { + return .failure(LoginError.appleError) + } + + let appleRequest = PostSocialLoginReqDTO( + socialType: "APPLE", + fcmToken: "", + socialAccessToken: "", + authorizationCode: result.authorizationCode, + idToken: result.identityToken + ) + + return .success(appleRequest) + } + + public func kakaoLoginResult() async throws -> Result { + let result = await loginManager.kakaoLogin() + + guard let result else { + return .failure(LoginError.kakaoError) + } + + let kakaoRequest = PostSocialLoginReqDTO( + socialType: "KAKAO", + fcmToken: "", + socialAccessToken: result.accessToken, + authorizationCode: "", + idToken: "" + ) + + return .success(kakaoRequest) + } } diff --git a/TnT/Projects/Presentation/Sources/Onbarding/Common/OnboardingFeature.swift b/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingFeature.swift similarity index 95% rename from TnT/Projects/Presentation/Sources/Onbarding/Common/OnboardingFeature.swift rename to TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingFeature.swift index 7ce8974c..1294e622 100644 --- a/TnT/Projects/Presentation/Sources/Onbarding/Common/OnboardingFeature.swift +++ b/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingFeature.swift @@ -9,11 +9,19 @@ import ComposableArchitecture import SwiftUI +import Domain +import DIContainer + @Reducer public struct OnboardingFeature { @ObservableState public struct State: Equatable { public var path = StackState() + public var userType: UserType? + public var nickname: String? = "" + public var socialType: LoginType? + public var termAgree: Bool = true + public var socialEmail: String? = "" public init(path: StackState = StackState()) { self.path = path diff --git a/TnT/Projects/Presentation/Sources/Onbarding/Common/OnboardingView.swift b/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingView.swift similarity index 100% rename from TnT/Projects/Presentation/Sources/Onbarding/Common/OnboardingView.swift rename to TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingView.swift diff --git a/TnT/Projects/Presentation/Sources/Onbarding/Common/TermFeature.swift b/TnT/Projects/Presentation/Sources/Onboarding/Common/TermFeature.swift similarity index 100% rename from TnT/Projects/Presentation/Sources/Onbarding/Common/TermFeature.swift rename to TnT/Projects/Presentation/Sources/Onboarding/Common/TermFeature.swift diff --git a/TnT/Projects/Presentation/Sources/Onbarding/Common/TermView.swift b/TnT/Projects/Presentation/Sources/Onboarding/Common/TermView.swift similarity index 100% rename from TnT/Projects/Presentation/Sources/Onbarding/Common/TermView.swift rename to TnT/Projects/Presentation/Sources/Onboarding/Common/TermView.swift diff --git a/TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Code/MakeInvitationCodeFeature.swift b/TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Code/MakeInvitationCodeFeature.swift similarity index 100% rename from TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Code/MakeInvitationCodeFeature.swift rename to TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Code/MakeInvitationCodeFeature.swift diff --git a/TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Code/MakeInvitationCodeView.swift b/TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Code/MakeInvitationCodeView.swift similarity index 100% rename from TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Code/MakeInvitationCodeView.swift rename to TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Code/MakeInvitationCodeView.swift diff --git a/TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Connection/ConnectedTraineeProfileFeature.swift b/TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Connection/ConnectedTraineeProfileFeature.swift similarity index 100% rename from TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Connection/ConnectedTraineeProfileFeature.swift rename to TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Connection/ConnectedTraineeProfileFeature.swift diff --git a/TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Connection/ConnectedTraineeProfileView.swift b/TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Connection/ConnectedTraineeProfileView.swift similarity index 100% rename from TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Connection/ConnectedTraineeProfileView.swift rename to TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Connection/ConnectedTraineeProfileView.swift diff --git a/TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Connection/ConnectionCompleteFeature.swift b/TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Connection/ConnectionCompleteFeature.swift similarity index 100% rename from TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Connection/ConnectionCompleteFeature.swift rename to TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Connection/ConnectionCompleteFeature.swift diff --git a/TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Connection/ConnectionCompleteView.swift b/TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Connection/ConnectionCompleteView.swift similarity index 100% rename from TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Connection/ConnectionCompleteView.swift rename to TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Connection/ConnectionCompleteView.swift diff --git a/TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Profile/TrainerSignUpCompleteFeature.swift b/TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Profile/TrainerSignUpCompleteFeature.swift similarity index 100% rename from TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Profile/TrainerSignUpCompleteFeature.swift rename to TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Profile/TrainerSignUpCompleteFeature.swift diff --git a/TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Profile/TrainerSignUpCompleteView.swift b/TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Profile/TrainerSignUpCompleteView.swift similarity index 100% rename from TnT/Projects/Presentation/Sources/Onbarding/Trainer/Login/Profile/TrainerSignUpCompleteView.swift rename to TnT/Projects/Presentation/Sources/Onboarding/Trainer/Login/Profile/TrainerSignUpCompleteView.swift From 0c947e153030ec4aeb03519f11dadc49d33c2377 Mon Sep 17 00:00:00 2001 From: Park Seo Yeon Date: Wed, 29 Jan 2025 19:48:29 +0900 Subject: [PATCH 6/9] =?UTF-8?q?[Feat]=20Firebase=20Info=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TnT/.gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TnT/.gitignore b/TnT/.gitignore index 8b1ed09a..e9e6c0d4 100644 --- a/TnT/.gitignore +++ b/TnT/.gitignore @@ -78,6 +78,6 @@ master.key # PList # => 전체 plist를 다 제외함. -# **/*.plist -# Support/*.plist +**/*.plist +Resources/GoogleService-Info.plist GoogleService-Info.plist \ No newline at end of file From cc0524d46b52352f586cb4defb46e3e20a1d2a29 Mon Sep 17 00:00:00 2001 From: Park Seo Yeon Date: Fri, 31 Jan 2025 16:45:18 +0900 Subject: [PATCH 7/9] =?UTF-8?q?[Feat]=20UseCase,=20UseCaseRepo=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC=20=ED=9B=84=20=EC=86=8C=EC=85=9C=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 테스트전 --- .../DIContainer/Sources/DIContainer.swift | 16 ++- .../SocialLogInRepositoryImpl.swift | 39 +++--- .../Service/User/UserRepositoryImpl.swift | 36 ------ .../Sources/Entity/SocailLoginEntity.swift | 119 ++++++++++++++++++ .../Sources/Mapper/PostSocialMapper.swift | 31 +++++ .../Sources/UseCase/SocialLoginUseCase.swift | 30 +++++ .../Domain/Sources/UseCase/UserUseCase.swift | 21 +++- .../Onboarding/Common/OnboardingFeature.swift | 67 +++++++++- TnT/Tuist/Package.resolved | 117 +++++++++++++++++ TnT/Tuist/Package.swift | 3 +- .../Dependency/DependencyInformation.swift | 3 +- .../Dependency/ExternalDependency.swift | 3 +- 12 files changed, 418 insertions(+), 67 deletions(-) create mode 100644 TnT/Projects/Domain/Sources/Entity/SocailLoginEntity.swift create mode 100644 TnT/Projects/Domain/Sources/Mapper/PostSocialMapper.swift create mode 100644 TnT/Projects/Domain/Sources/UseCase/SocialLoginUseCase.swift diff --git a/TnT/Projects/DIContainer/Sources/DIContainer.swift b/TnT/Projects/DIContainer/Sources/DIContainer.swift index 4f7a83dc..36c756bd 100644 --- a/TnT/Projects/DIContainer/Sources/DIContainer.swift +++ b/TnT/Projects/DIContainer/Sources/DIContainer.swift @@ -12,7 +12,12 @@ import Data // MARK: - Swift-Dependencies private enum UserUseCaseKey: DependencyKey { - static let liveValue: UserUseCase = DefaultUserUseCase(userRepository: UserRepositoryImpl()) +// static let liveValue: UserUseCase + static let liveValue: UserUseCase = DefaultUserUseCase(userRepostiory: UserRepositoryImpl()) +} + +private enum UserUseCaseRepoKey: DependencyKey { + static let liveValue: UserRepository = DefaultUserUseCase(userRepostiory: UserRepositoryImpl()) } private enum TraineeUseCaseKey: DependencyKey { @@ -23,7 +28,7 @@ private enum TraineeUseCaseKey: DependencyKey { } private enum SocialUseCaseKey: DependencyKey { - static let liveValue: SocialLoginUserCase = DefaultSocialLoginUserCase(socialLoginRepository: SocialLogInRepositoryImpl()) + static let liveValue: SocialLoginUseCase = SocialLoginUseCase(socialLoginRepository: SocialLogInRepositoryImpl(loginManager: SNSLoginManager())) } // MARK: - DependencyValues @@ -33,12 +38,17 @@ public extension DependencyValues { set { self[UserUseCaseKey.self] = newValue } } + var userUseRepoCase: UserRepository { + get { self[UserUseCaseRepoKey.self] } + set { self[UserUseCaseRepoKey.self] = newValue } + } + var traineeUseCase: TraineeUseCase { get { self[TraineeUseCaseKey.self] } set { self[TraineeUseCaseKey.self] = newValue } } - var socialLogInUseCase: SocialLoginUserCase { + var socialLogInUseCase: SocialLoginUseCase { get { self[SocialUseCaseKey.self] } set { self[SocialUseCaseKey.self] = newValue } } diff --git a/TnT/Projects/Data/Sources/Network/Service/SocialLogin/SocialLogInRepositoryImpl.swift b/TnT/Projects/Data/Sources/Network/Service/SocialLogin/SocialLogInRepositoryImpl.swift index 0e0295c1..009f07c1 100644 --- a/TnT/Projects/Data/Sources/Network/Service/SocialLogin/SocialLogInRepositoryImpl.swift +++ b/TnT/Projects/Data/Sources/Network/Service/SocialLogin/SocialLogInRepositoryImpl.swift @@ -19,12 +19,29 @@ public enum LoginError: Error { case unknown(message: String) } -//public struct SocialLogInRepositoryImpl: UserRepository { -// -// public let loginManager = SNSLoginManager() -// -// public init() {} -// +public struct SocialLogInRepositoryImpl: SocialLoginRepository { + + public let loginManager: SNSLoginManager + + public init(loginManager: SNSLoginManager) { + self.loginManager = loginManager + } + + public func appleLogin() async -> AppleLoginInfo? { + let result = await loginManager.appleLogin() + + return result + } + + public func kakaoLogin() async -> KakaoLoginInfo? { + let result = await loginManager.kakaoLogin() + return result + } + + public func kakaoLogout() async { + // 미구현 + } + // public func appleLoginResult() async throws -> Result { // let result = await loginManager.appleLogin() // @@ -60,12 +77,4 @@ public enum LoginError: Error { // // return .success(kakaoRequest) // } -// -// public func postSocialLogin(_ reqDTO: Domain.PostSocialLoginReqDTO) async throws -> PostSocialLoginResDTO { -// -// } -// -// public func postSignUp(_ reqDTO: Domain.PostSignUpReqDTO, profileImage: Data?) async throws -> PostSignUpResDTO { -// <#code#> -// } -//} +} diff --git a/TnT/Projects/Data/Sources/Network/Service/User/UserRepositoryImpl.swift b/TnT/Projects/Data/Sources/Network/Service/User/UserRepositoryImpl.swift index 23746e17..d25e473e 100644 --- a/TnT/Projects/Data/Sources/Network/Service/User/UserRepositoryImpl.swift +++ b/TnT/Projects/Data/Sources/Network/Service/User/UserRepositoryImpl.swift @@ -36,40 +36,4 @@ public struct UserRepositoryImpl: UserRepository { decodingType: PostSignUpResDTO.self ) } - - public func appleLoginResult() async throws -> Result { - let result = await loginManager.appleLogin() - - guard let result else { - return .failure(LoginError.appleError) - } - - let appleRequest = PostSocialLoginReqDTO( - socialType: "APPLE", - fcmToken: "", - socialAccessToken: "", - authorizationCode: result.authorizationCode, - idToken: result.identityToken - ) - - return .success(appleRequest) - } - - public func kakaoLoginResult() async throws -> Result { - let result = await loginManager.kakaoLogin() - - guard let result else { - return .failure(LoginError.kakaoError) - } - - let kakaoRequest = PostSocialLoginReqDTO( - socialType: "KAKAO", - fcmToken: "", - socialAccessToken: result.accessToken, - authorizationCode: "", - idToken: "" - ) - - return .success(kakaoRequest) - } } diff --git a/TnT/Projects/Domain/Sources/Entity/SocailLoginEntity.swift b/TnT/Projects/Domain/Sources/Entity/SocailLoginEntity.swift new file mode 100644 index 00000000..732aa9df --- /dev/null +++ b/TnT/Projects/Domain/Sources/Entity/SocailLoginEntity.swift @@ -0,0 +1,119 @@ +// +// PostSocailEntity.swift +// Domain +// +// Created by 박서연 on 1/31/25. +// Copyright © 2025 yapp25thTeamTnT. All rights reserved. +// + +import Foundation + +/// 소셜 로그인 요청 DTO +public struct PostSocailEntity: Equatable { + /// 소셜 로그인 타입 (KAKAO, APPLE) + let socialType: String + /// FCM 토큰 + let fcmToken: String + /// 소셜 액세스 토큰 + let socialAccessToken: String? + /// 애플 인가 코드 (Apple 로그인 시 필요) + let authorizationCode: String? + /// 애플 ID 토큰 (Apple 로그인 시 필요) + let idToken: String? + + public init( + socialType: String, + fcmToken: String, + socialAccessToken: String?, + authorizationCode: String?, + idToken: String? + ) { + self.socialType = socialType + self.fcmToken = fcmToken + self.socialAccessToken = socialAccessToken + self.authorizationCode = authorizationCode + self.idToken = idToken + } +} + +/// 소셜 로그인 응답 DTO +public struct PostSocialLoginResEntity: Equatable { + /// 세션 ID + public let sessionId: String? + /// 소셜 로그인 ID + public let socialId: String + /// 소셜 이메일 + public let socialEmail: String + /// 소셜 로그인 타입 (KAKAO, APPLE) + public let socialType: String + /// 가입 여부 (`true`: 이미 가입됨, `false`: 미가입) + public let isSignUp: Bool +} + +/// 회원가입 요청 DTO +public struct PostSignUpReqEntity { + /// FCM 토큰 + let fcmToken: String + /// 회원 타입 (trainer, trainee) + let memberType: String + /// 소셜 로그인 타입 (KAKAO, APPLE) + let socialType: String + /// 소셜 로그인 ID + let socialId: String + /// 소셜 로그인 이메일 + let socialEmail: String + /// 서비스 이용 약관 동의 여부 + let serviceAgreement: Bool + /// 개인정보 수집 동의 여부 + let collectionAgreement: Bool + /// 광고성 알림 수신 동의 여부 + let advertisementAgreement: Bool + /// 푸시 알림 수신 동의 여부 + let pushAgreement: Bool + /// 회원 이름 + let name: String + /// 생년월일 (yyyy-MM-dd) + let birthday: String? + /// 키 (cm) + let height: Double? + /// 몸무게 (kg, 소수점 1자리까지 가능) + let weight: Double? + /// 트레이너에게 전달할 주의사항 + let cautionNote: String? + /// PT 목적 (체중 감량, 근력 향상 등) + let goalContents: [String]? + + public init( + fcmToken: String, + memberType: String, + socialType: String, + socialId: String, + socialEmail: String, + serviceAgreement: Bool, + collectionAgreement: Bool, + advertisementAgreement: Bool, + pushAgreement: Bool, + name: String, + birthday: String?, + height: Double?, + weight: Double?, + cautionNote: String?, + goalContents: [String]? + ) { + self.fcmToken = fcmToken + self.memberType = memberType + self.socialType = socialType + self.socialId = socialId + self.socialEmail = socialEmail + self.serviceAgreement = serviceAgreement + self.collectionAgreement = collectionAgreement + self.advertisementAgreement = advertisementAgreement + self.pushAgreement = pushAgreement + self.name = name + self.birthday = birthday + self.height = height + self.weight = weight + self.cautionNote = cautionNote + self.goalContents = goalContents + } +} diff --git a/TnT/Projects/Domain/Sources/Mapper/PostSocialMapper.swift b/TnT/Projects/Domain/Sources/Mapper/PostSocialMapper.swift new file mode 100644 index 00000000..49310037 --- /dev/null +++ b/TnT/Projects/Domain/Sources/Mapper/PostSocialMapper.swift @@ -0,0 +1,31 @@ +// +// PostSocialMapper.swift +// Domain +// +// Created by 박서연 on 1/31/25. +// Copyright © 2025 yapp25thTeamTnT. All rights reserved. +// + +import Foundation + +public struct PostSocialMapper { + public static func toDTO(from entity: PostSocailEntity) -> PostSocialLoginReqDTO { + return PostSocialLoginReqDTO( + socialType: entity.socialType, + fcmToken: entity.fcmToken, + socialAccessToken: entity.socialAccessToken, + authorizationCode: entity.authorizationCode, + idToken: entity.idToken + ) + } + + public static func toEntity(from dto: PostSocialLoginReqDTO) -> PostSocailEntity { + return PostSocailEntity( + socialType: dto.socialType, + fcmToken: dto.fcmToken, + socialAccessToken: dto.socialAccessToken, + authorizationCode: dto.authorizationCode, + idToken: dto.idToken + ) + } +} diff --git a/TnT/Projects/Domain/Sources/UseCase/SocialLoginUseCase.swift b/TnT/Projects/Domain/Sources/UseCase/SocialLoginUseCase.swift new file mode 100644 index 00000000..17f2ab67 --- /dev/null +++ b/TnT/Projects/Domain/Sources/UseCase/SocialLoginUseCase.swift @@ -0,0 +1,30 @@ +// +// SocialLoginUseCase.swift +// Domain +// +// Created by 박서연 on 1/29/25. +// Copyright © 2025 yapp25thTeamTnT. All rights reserved. +// + +import Foundation + +public struct SocialLoginUseCase { + + private let socialLoginRepository: SocialLoginRepository + + public init(socialLoginRepository: SocialLoginRepository) { + self.socialLoginRepository = socialLoginRepository + } + + public func appleLogin() async -> AppleLoginInfo? { + return await socialLoginRepository.appleLogin() + } + + public func kakaoLogin() async -> KakaoLoginInfo? { + return await socialLoginRepository.kakaoLogin() + } + + public func kakaoLogout() async { + // 미구현 + } +} diff --git a/TnT/Projects/Domain/Sources/UseCase/UserUseCase.swift b/TnT/Projects/Domain/Sources/UseCase/UserUseCase.swift index 852f9772..b4db8f42 100644 --- a/TnT/Projects/Domain/Sources/UseCase/UserUseCase.swift +++ b/TnT/Projects/Domain/Sources/UseCase/UserUseCase.swift @@ -7,6 +7,7 @@ // import Dependencies +import SwiftUI // MARK: - UserUseCase 프로토콜 public protocol UserUseCase { @@ -27,13 +28,15 @@ public protocol UserUseCase { } // MARK: - Default 구현체 -public struct DefaultUserUseCase: UserUseCase { - private let userRepository: UserRepository +public struct DefaultUserUseCase: UserRepository, UserUseCase { - public init(userRepository: UserRepository) { - self.userRepository = userRepository + public let userRepostiory: UserRepository + + public init(userRepostiory: UserRepository) { + self.userRepostiory = userRepostiory } + // MARK: - Usecase public func validateUserName(_ name: String) -> Bool { return !name.isEmpty && UserPolicy.userNameInput.textValidation(name) } @@ -61,4 +64,14 @@ public struct DefaultUserUseCase: UserUseCase { public func getPrecautionMaxLength() -> Int { return UserPolicy.maxPrecautionLength } + + // MARK: - Repository + public func postSocialLogin(_ reqDTO: PostSocialLoginReqDTO) async throws -> PostSocialLoginResDTO { + return try await userRepostiory.postSocialLogin(reqDTO) + } + + public func postSignUp(_ reqDTO: PostSignUpReqDTO, profileImage: Data?) async throws -> PostSignUpResDTO { + return try await userRepostiory.postSignUp(reqDTO, profileImage: profileImage) + } + } diff --git a/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingFeature.swift b/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingFeature.swift index 1294e622..8753e0e1 100644 --- a/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingFeature.swift +++ b/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingFeature.swift @@ -22,12 +22,17 @@ public struct OnboardingFeature { public var socialType: LoginType? public var termAgree: Bool = true public var socialEmail: String? = "" + public var postUserEntity: PostSocailEntity? public init(path: StackState = StackState()) { self.path = path } } + @Dependency(\.userUseCase) private var userUseCase: UserUseCase + @Dependency(\.userUseRepoCase) private var userUseCaseRepo: UserRepository + @Dependency(\.socialLogInUseCase) private var socialLoginUseCase: SocialLoginUseCase + public enum Action: ViewAction { /// 뷰에서 일어나는 액션을 처리합니다.(카카오,애플로그인 실행) case view(View) @@ -40,6 +45,9 @@ public struct OnboardingFeature { public enum View: Equatable { case tappedAppleLogin case tappedKakaoLogin + case postSocialLogin(entity: PostSocailEntity) + case postSignUp + case socailLoginFail } @CasePathable @@ -64,6 +72,8 @@ public struct OnboardingFeature { case toRegisterInvitationCode /// 트레이니의 pt 횟수 및 정보 입력화면으로 이동 case toRegisterPtClassInfo + /// 홈으로 이동 + case toHome } } @@ -75,10 +85,53 @@ public struct OnboardingFeature { case let .view(view): switch view { case .tappedAppleLogin: - state.path.append(.term(TermFeature.State())) - return .none + return .run { @Sendable send in + let result = await socialLoginUseCase.appleLogin() + guard let result else { return } + let entity = PostSocailEntity( + socialType: "APPLE", + fcmToken: "", + socialAccessToken: "", + authorizationCode: result.authorizationCode, + idToken: result.identityToken + ) + + await send(.view(.postSocialLogin(entity: entity))) + } + case .tappedKakaoLogin: - state.path.append(.term(TermFeature.State())) + return .run { @Sendable send in + let result = await socialLoginUseCase.kakaoLogin() + guard let result else { return } + + let entity = PostSocailEntity( + socialType: "KAKAO", + fcmToken: "", + socialAccessToken: result.accessToken, + authorizationCode: "", + idToken: "" + ) + + await send(.view(.postSocialLogin(entity: entity))) + } + + case .postSocialLogin: + guard let postEntity = state.postUserEntity else { return .none } + let entity = PostSocialMapper.toDTO(from: postEntity) + + return .run { send in + do { + let result = try await userUseCaseRepo.postSocialLogin(entity) + await send(.move(.toHome)) + } catch { + await send(.move(.toTermview)) + } + } + + case .socailLoginFail: + return .none + + case .postSignUp: return .none } @@ -99,6 +152,9 @@ public struct OnboardingFeature { case .toMakeInvitationCode: state.path.append(.makeInvitationCode(MakeInvitationCodeFeature.State())) return .none + case .toHome: + print("post 사인 성공..") + return .none default: return .none } @@ -123,9 +179,6 @@ public struct OnboardingFeature { default: return .none } - - default: - return .none } } .forEach(\.path, action: \.path) @@ -153,5 +206,7 @@ public struct OnboardingFeature { case registerInvitationCode /// 트레이니 수업 정보 입력 case registerPtClassInfo + /// 홈 + case home } } diff --git a/TnT/Tuist/Package.resolved b/TnT/Tuist/Package.resolved index 14c7aa10..77d93e06 100644 --- a/TnT/Tuist/Package.resolved +++ b/TnT/Tuist/Package.resolved @@ -1,5 +1,14 @@ { "pins" : [ + { + "identity" : "abseil-cpp-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/abseil-cpp-binary.git", + "state" : { + "revision" : "194a6706acbd25e4ef639bcaddea16e8758a3e27", + "version" : "1.2024011602.0" + } + }, { "identity" : "alamofire", "kind" : "remoteSourceControl", @@ -9,6 +18,15 @@ "version" : "5.10.2" } }, + { + "identity" : "app-check", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/app-check.git", + "state" : { + "revision" : "61b85103a1aeed8218f17c794687781505fbbef5", + "version" : "11.2.0" + } + }, { "identity" : "combine-schedulers", "kind" : "remoteSourceControl", @@ -18,6 +36,69 @@ "version" : "1.0.3" } }, + { + "identity" : "firebase-ios-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/firebase-ios-sdk", + "state" : { + "revision" : "0d885d28250fb1196b614bc9455079b75c531f72", + "version" : "11.7.0" + } + }, + { + "identity" : "googleappmeasurement", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleAppMeasurement.git", + "state" : { + "revision" : "be0881ff728eca210ccb628092af400c086abda3", + "version" : "11.7.0" + } + }, + { + "identity" : "googledatatransport", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleDataTransport.git", + "state" : { + "revision" : "617af071af9aa1d6a091d59a202910ac482128f9", + "version" : "10.1.0" + } + }, + { + "identity" : "googleutilities", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleUtilities.git", + "state" : { + "revision" : "53156c7ec267db846e6b64c9f4c4e31ba4cf75eb", + "version" : "8.0.2" + } + }, + { + "identity" : "grpc-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/grpc-binary.git", + "state" : { + "revision" : "f56d8fc3162de9a498377c7b6cea43431f4f5083", + "version" : "1.65.1" + } + }, + { + "identity" : "gtm-session-fetcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/gtm-session-fetcher.git", + "state" : { + "revision" : "3cdb78efb79b4a5383c3911488d8025bfc545b5e", + "version" : "4.3.0" + } + }, + { + "identity" : "interop-ios-for-google-sdks", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/interop-ios-for-google-sdks.git", + "state" : { + "revision" : "2d12673670417654f08f5f90fdd62926dc3a2648", + "version" : "100.0.0" + } + }, { "identity" : "kakao-ios-sdk", "kind" : "remoteSourceControl", @@ -27,6 +108,15 @@ "version" : "2.23.0" } }, + { + "identity" : "leveldb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/leveldb.git", + "state" : { + "revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1", + "version" : "1.22.5" + } + }, { "identity" : "lottie-ios", "kind" : "remoteSourceControl", @@ -36,6 +126,24 @@ "version" : "4.5.1" } }, + { + "identity" : "nanopb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/nanopb.git", + "state" : { + "revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1", + "version" : "2.30910.0" + } + }, + { + "identity" : "promises", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/promises.git", + "state" : { + "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", + "version" : "2.4.0" + } + }, { "identity" : "swift-case-paths", "kind" : "remoteSourceControl", @@ -126,6 +234,15 @@ "version" : "1.4.1" } }, + { + "identity" : "swift-protobuf", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-protobuf.git", + "state" : { + "revision" : "ebc7251dd5b37f627c93698e4374084d98409633", + "version" : "1.28.2" + } + }, { "identity" : "swift-sharing", "kind" : "remoteSourceControl", diff --git a/TnT/Tuist/Package.swift b/TnT/Tuist/Package.swift index 492314c3..f7416003 100644 --- a/TnT/Tuist/Package.swift +++ b/TnT/Tuist/Package.swift @@ -17,6 +17,7 @@ let package = Package( .package(url: "https://github.com/pointfreeco/swift-composable-architecture", from: "1.17.0"), .package(url: "https://github.com/airbnb/lottie-ios", from: "4.5.0"), .package(url: "https://github.com/kakao/kakao-ios-sdk.git", from: "2.20.0"), - .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.6.3") + .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.6.3"), + .package(url: "https://github.com/firebase/firebase-ios-sdk", from: "11.7.0") ] ) diff --git a/TnT/Tuist/ProjectDescriptionHelpers/Dependency/DependencyInformation.swift b/TnT/Tuist/ProjectDescriptionHelpers/Dependency/DependencyInformation.swift index 7da5ee1c..607b6e10 100644 --- a/TnT/Tuist/ProjectDescriptionHelpers/Dependency/DependencyInformation.swift +++ b/TnT/Tuist/ProjectDescriptionHelpers/Dependency/DependencyInformation.swift @@ -11,7 +11,7 @@ let dependencyInfo: [DependencyInformation: [DependencyInformation]] = [ .TnTApp: [.Presentation, .Data], .Presentation: [.DIContainer, .DesignSystem, .Domain, .ComposableArchitecture], .Domain: [.SwiftDepedencies], - .Data: [.Domain, .KakaoSDKUser, .SwiftDepedencies], + .Data: [.Domain, .KakaoSDKUser, .SwiftDepedencies, .FirebaseMessaging], .DesignSystem: [.Lottie], .DIContainer: [.Domain, .Data] ] @@ -27,6 +27,7 @@ public enum DependencyInformation: String, CaseIterable, Sendable { case ComposableArchitecture = "ComposableArchitecture" case KakaoSDKUser = "KakaoSDKUser" case SwiftDepedencies = "Dependencies" + case FirebaseMessaging = "FirebaseMessaging" } public extension DependencyInformation { diff --git a/TnT/Tuist/ProjectDescriptionHelpers/Dependency/ExternalDependency.swift b/TnT/Tuist/ProjectDescriptionHelpers/Dependency/ExternalDependency.swift index 32da25b8..622ff1d6 100644 --- a/TnT/Tuist/ProjectDescriptionHelpers/Dependency/ExternalDependency.swift +++ b/TnT/Tuist/ProjectDescriptionHelpers/Dependency/ExternalDependency.swift @@ -11,5 +11,6 @@ let externalDependency: [DependencyInformation] = [ .KakaoSDKUser, .Lottie, .ComposableArchitecture, - .SwiftDepedencies + .SwiftDepedencies, + .FirebaseMessaging ] From d92ce5d46cac5582b685faa8a4c1ddd83717c29a Mon Sep 17 00:00:00 2001 From: Park Seo Yeon Date: Sat, 1 Feb 2025 03:13:56 +0900 Subject: [PATCH 8/9] [Feat] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 소셜 로그인 postSingIn까지 구현 완료 - 추후 데이터 연결을 어찌할지? --- .../Sources/Mapper/PostSocialMapper.swift | 22 +++++++++++++++++++ .../Onboarding/Common/OnboardingFeature.swift | 9 ++++---- .../Onboarding/Common/OnboardingView.swift | 6 ++--- .../Onboarding/Common/TermFeature.swift | 9 ++++++-- .../Sources/Onboarding/Common/TermView.swift | 6 ++--- TnT/Projects/TnTApp/Sources/ContentView.swift | 5 +++-- TnT/Projects/TnTApp/Sources/TnTApp.swift | 7 +++++- TnT/Projects/TnTApp/TnTApp.entitlements | 10 +++++++++ .../Project/Target+Templates.swift | 1 + 9 files changed, 58 insertions(+), 17 deletions(-) create mode 100644 TnT/Projects/TnTApp/TnTApp.entitlements diff --git a/TnT/Projects/Domain/Sources/Mapper/PostSocialMapper.swift b/TnT/Projects/Domain/Sources/Mapper/PostSocialMapper.swift index 49310037..c299a03a 100644 --- a/TnT/Projects/Domain/Sources/Mapper/PostSocialMapper.swift +++ b/TnT/Projects/Domain/Sources/Mapper/PostSocialMapper.swift @@ -28,4 +28,26 @@ public struct PostSocialMapper { idToken: dto.idToken ) } + + /// `PostSocialLoginResDTO` → `PostSocialLoginResEntity` 변환 + public static func toResEntity(from dto: PostSocialLoginResDTO) -> PostSocialLoginResEntity { + return PostSocialLoginResEntity( + sessionId: dto.sessionId, + socialId: dto.socialId, + socialEmail: dto.socialEmail, + socialType: dto.socialType, + isSignUp: dto.isSignUp + ) + } + + /// `PostSocialLoginResEntity` → `PostSocialLoginResDTO` 변환 + public static func toDTO(from entity: PostSocialLoginResEntity) -> PostSocialLoginResDTO { + return PostSocialLoginResDTO( + sessionId: entity.sessionId, + socialId: entity.socialId, + socialEmail: entity.socialEmail, + socialType: entity.socialType, + isSignUp: entity.isSignUp + ) + } } diff --git a/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingFeature.swift b/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingFeature.swift index 8753e0e1..637777d3 100644 --- a/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingFeature.swift +++ b/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingFeature.swift @@ -115,13 +115,11 @@ public struct OnboardingFeature { await send(.view(.postSocialLogin(entity: entity))) } - case .postSocialLogin: - guard let postEntity = state.postUserEntity else { return .none } - let entity = PostSocialMapper.toDTO(from: postEntity) - + case .postSocialLogin(let entity): + let post = PostSocialMapper.toDTO(from: entity) return .run { send in do { - let result = try await userUseCaseRepo.postSocialLogin(entity) + let result = try await userUseCaseRepo.postSocialLogin(post) await send(.move(.toHome)) } catch { await send(.move(.toTermview)) @@ -140,6 +138,7 @@ public struct OnboardingFeature { case .toTermview: state.path.append(.term(TermFeature.State())) return .none + case .toselectRole: state.path.append(.selectRole) return .none diff --git a/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingView.swift b/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingView.swift index d1b7693f..9bd2ca0e 100644 --- a/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingView.swift +++ b/TnT/Projects/Presentation/Sources/Onboarding/Common/OnboardingView.swift @@ -84,10 +84,8 @@ public struct OnboardingView: View { .background(type.background) .clipShape(RoundedRectangle(cornerRadius: 12)) .onTapGesture { - store.send(.move(.toTermview)) -// type == .kakao -// ? store.send(.view(.tappedAppleLogin)) -// : store.send(.view(.tappedAppleLogin)) + type == .kakao ? store.send(.view(.tappedKakaoLogin)) + : store.send(.view(.tappedAppleLogin)) } } } diff --git a/TnT/Projects/Presentation/Sources/Onboarding/Common/TermFeature.swift b/TnT/Projects/Presentation/Sources/Onboarding/Common/TermFeature.swift index 8c4e8ae3..47029d8c 100644 --- a/TnT/Projects/Presentation/Sources/Onboarding/Common/TermFeature.swift +++ b/TnT/Projects/Presentation/Sources/Onboarding/Common/TermFeature.swift @@ -13,13 +13,18 @@ import ComposableArchitecture public struct TermFeature { @ObservableState public struct State: Equatable { - var view_terms: [Term: Bool] = [:] + var view_terms: [Term: Bool] var view_isAllAgreed: Bool { view_terms.values.allSatisfy { $0 } } var isNavigaiton: Bool = false - public init() { } + public init() { + self.view_terms = [ + .term: false, + .personalInfo: false + ] + } } public enum Action: Equatable, ViewAction { diff --git a/TnT/Projects/Presentation/Sources/Onboarding/Common/TermView.swift b/TnT/Projects/Presentation/Sources/Onboarding/Common/TermView.swift index 23a1a313..ea24ce83 100644 --- a/TnT/Projects/Presentation/Sources/Onboarding/Common/TermView.swift +++ b/TnT/Projects/Presentation/Sources/Onboarding/Common/TermView.swift @@ -12,7 +12,7 @@ import ComposableArchitecture import DesignSystem @ViewAction(for: TermFeature.self) -struct TermView: View { +public struct TermView: View { public let store: StoreOf @@ -20,7 +20,7 @@ struct TermView: View { self.store = store } - var body: some View { + public var body: some View { VStack { VStack(alignment: .leading, spacing: 0) { Header() @@ -80,7 +80,7 @@ struct TermView: View { term: term, isAgreed: store.view_terms[term] ?? false ) { - send(.toggleTerm(term, $0)) + send(.toggleTerm(term, !$0)) } } } diff --git a/TnT/Projects/TnTApp/Sources/ContentView.swift b/TnT/Projects/TnTApp/Sources/ContentView.swift index 2bf81b08..dd422ce6 100644 --- a/TnT/Projects/TnTApp/Sources/ContentView.swift +++ b/TnT/Projects/TnTApp/Sources/ContentView.swift @@ -12,8 +12,9 @@ import ComposableArchitecture struct ContentView: View { var body: some View { - Text("dasdasdf") - Text("Hello, World!") + TermView(store: Store(initialState: TermFeature.State(), reducer: { + TermFeature() + })) } } diff --git a/TnT/Projects/TnTApp/Sources/TnTApp.swift b/TnT/Projects/TnTApp/Sources/TnTApp.swift index ca9ab0fc..4f0825b3 100644 --- a/TnT/Projects/TnTApp/Sources/TnTApp.swift +++ b/TnT/Projects/TnTApp/Sources/TnTApp.swift @@ -8,6 +8,8 @@ import SwiftUI +import Presentation +import ComposableArchitecture import DesignSystem @main @@ -19,7 +21,10 @@ struct ToyProjectApp: App { var body: some Scene { WindowGroup { - ContentView() + OnboardingView(store: Store(initialState: OnboardingFeature.State(), reducer: { + OnboardingFeature() + })) +// ContentView() } } } diff --git a/TnT/Projects/TnTApp/TnTApp.entitlements b/TnT/Projects/TnTApp/TnTApp.entitlements new file mode 100644 index 00000000..a812db50 --- /dev/null +++ b/TnT/Projects/TnTApp/TnTApp.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.developer.applesignin + + Default + + + diff --git a/TnT/Tuist/ProjectDescriptionHelpers/Project/Target+Templates.swift b/TnT/Tuist/ProjectDescriptionHelpers/Project/Target+Templates.swift index 4be59b37..b2629b78 100644 --- a/TnT/Tuist/ProjectDescriptionHelpers/Project/Target+Templates.swift +++ b/TnT/Tuist/ProjectDescriptionHelpers/Project/Target+Templates.swift @@ -68,6 +68,7 @@ public extension Target { infoPlist: .file(path: .relativeToRoot("Tuist/Config/Info.plist")), sources: ["Sources/**"], resources: ["Resources/**"], + entitlements: "\(appName).entitlements", scripts: [.swiftLint], dependencies: dependencies ) From bd3f5c591e394465fbea39ae205c92acb2e2c6c3 Mon Sep 17 00:00:00 2001 From: Park Seo Yeon Date: Wed, 5 Feb 2025 03:38:54 +0900 Subject: [PATCH 9/9] =?UTF-8?q?[Refactor]=20=EB=A6=AC=EB=B7=B0=20=EB=82=B4?= =?UTF-8?q?=EC=9A=A9=20=EB=B0=98=EC=98=81,=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20=EB=B0=8F=20=EC=BD=94=EB=93=9C=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC,=20ignore=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 -- TnT/.gitignore | 3 -- .../DIContainer/Sources/DIContainer.swift | 5 --- .../SocialLogInRepositoryImpl.swift | 40 ------------------- .../Service/User/UserRepositoryImpl.swift | 1 - .../Repository/SocialLoginRepository.swift | 2 - .../Sources/UseCase/SocialLoginUseCase.swift | 4 -- .../Domain/Sources/UseCase/UserUseCase.swift | 12 +++--- 8 files changed, 7 insertions(+), 63 deletions(-) diff --git a/.gitignore b/.gitignore index 8b1ed09a..9af8aebe 100644 --- a/.gitignore +++ b/.gitignore @@ -77,7 +77,4 @@ Signing/ master.key # PList -# => 전체 plist를 다 제외함. -# **/*.plist -# Support/*.plist GoogleService-Info.plist \ No newline at end of file diff --git a/TnT/.gitignore b/TnT/.gitignore index e9e6c0d4..9af8aebe 100644 --- a/TnT/.gitignore +++ b/TnT/.gitignore @@ -77,7 +77,4 @@ Signing/ master.key # PList -# => 전체 plist를 다 제외함. -**/*.plist -Resources/GoogleService-Info.plist GoogleService-Info.plist \ No newline at end of file diff --git a/TnT/Projects/DIContainer/Sources/DIContainer.swift b/TnT/Projects/DIContainer/Sources/DIContainer.swift index 2c7754f9..e0c12763 100644 --- a/TnT/Projects/DIContainer/Sources/DIContainer.swift +++ b/TnT/Projects/DIContainer/Sources/DIContainer.swift @@ -12,7 +12,6 @@ import Data // MARK: - Swift-Dependencies private enum UserUseCaseKey: DependencyKey { -// static let liveValue: UserUseCase static let liveValue: UserUseCase = DefaultUserUseCase(userRepostiory: UserRepositoryImpl()) } @@ -31,10 +30,6 @@ private enum SocialUseCaseKey: DependencyKey { static let liveValue: SocialLoginUseCase = SocialLoginUseCase(socialLoginRepository: SocialLogInRepositoryImpl(loginManager: SNSLoginManager())) } -private enum SocialUseCaseKey: DependencyKey { - static let liveValue: SocialLoginUseCase = SocialLoginUseCase(socialLoginRepository: SocialLogInRepositoryImpl(loginManager: SNSLoginManager())) -} - // MARK: - DependencyValues public extension DependencyValues { var userUseCase: UserUseCase { diff --git a/TnT/Projects/Data/Sources/Network/Service/SocialLogin/SocialLogInRepositoryImpl.swift b/TnT/Projects/Data/Sources/Network/Service/SocialLogin/SocialLogInRepositoryImpl.swift index 009f07c1..51adca91 100644 --- a/TnT/Projects/Data/Sources/Network/Service/SocialLogin/SocialLogInRepositoryImpl.swift +++ b/TnT/Projects/Data/Sources/Network/Service/SocialLogin/SocialLogInRepositoryImpl.swift @@ -37,44 +37,4 @@ public struct SocialLogInRepositoryImpl: SocialLoginRepository { let result = await loginManager.kakaoLogin() return result } - - public func kakaoLogout() async { - // 미구현 - } - -// public func appleLoginResult() async throws -> Result { -// let result = await loginManager.appleLogin() -// -// guard let result else { -// return .failure(LoginError.appleError) -// } -// -// let appleRequest = PostSocialLoginReqDTO( -// socialType: "APPLE", -// fcmToken: "", -// socialAccessToken: "", -// authorizationCode: result.authorizationCode, -// idToken: result.identityToken -// ) -// -// return .success(appleRequest) -// } -// -// public func kakaoLoginResult() async throws -> Result { -// let result = await loginManager.kakaoLogin() -// -// guard let result else { -// return .failure(LoginError.kakaoError) -// } -// -// let kakaoRequest = PostSocialLoginReqDTO( -// socialType: "KAKAO", -// fcmToken: "", -// socialAccessToken: result.accessToken, -// authorizationCode: "", -// idToken: "" -// ) -// -// return .success(kakaoRequest) -// } } diff --git a/TnT/Projects/Data/Sources/Network/Service/User/UserRepositoryImpl.swift b/TnT/Projects/Data/Sources/Network/Service/User/UserRepositoryImpl.swift index d25e473e..251e35ea 100644 --- a/TnT/Projects/Data/Sources/Network/Service/User/UserRepositoryImpl.swift +++ b/TnT/Projects/Data/Sources/Network/Service/User/UserRepositoryImpl.swift @@ -13,7 +13,6 @@ import Domain /// 사용자 관련 네트워크 요청을 처리하는 UserRepository 구현체 public struct UserRepositoryImpl: UserRepository { - private let loginManager = SNSLoginManager() private let networkService: NetworkService = .shared public init() {} diff --git a/TnT/Projects/Domain/Sources/Repository/SocialLoginRepository.swift b/TnT/Projects/Domain/Sources/Repository/SocialLoginRepository.swift index 57e0aab0..38abc6d3 100644 --- a/TnT/Projects/Domain/Sources/Repository/SocialLoginRepository.swift +++ b/TnT/Projects/Domain/Sources/Repository/SocialLoginRepository.swift @@ -14,6 +14,4 @@ public protocol SocialLoginRepository { func appleLogin() async -> AppleLoginInfo? /// 카카오 로그인을 수행합니다 func kakaoLogin() async -> KakaoLoginInfo? - /// 카카오 로그아웃을 수행합니다 - func kakaoLogout() async } diff --git a/TnT/Projects/Domain/Sources/UseCase/SocialLoginUseCase.swift b/TnT/Projects/Domain/Sources/UseCase/SocialLoginUseCase.swift index 17f2ab67..6eea308b 100644 --- a/TnT/Projects/Domain/Sources/UseCase/SocialLoginUseCase.swift +++ b/TnT/Projects/Domain/Sources/UseCase/SocialLoginUseCase.swift @@ -23,8 +23,4 @@ public struct SocialLoginUseCase { public func kakaoLogin() async -> KakaoLoginInfo? { return await socialLoginRepository.kakaoLogin() } - - public func kakaoLogout() async { - // 미구현 - } } diff --git a/TnT/Projects/Domain/Sources/UseCase/UserUseCase.swift b/TnT/Projects/Domain/Sources/UseCase/UserUseCase.swift index b4db8f42..e83b8c3d 100644 --- a/TnT/Projects/Domain/Sources/UseCase/UserUseCase.swift +++ b/TnT/Projects/Domain/Sources/UseCase/UserUseCase.swift @@ -7,7 +7,7 @@ // import Dependencies -import SwiftUI +import Foundation // MARK: - UserUseCase 프로토콜 public protocol UserUseCase { @@ -64,14 +64,16 @@ public struct DefaultUserUseCase: UserRepository, UserUseCase { public func getPrecautionMaxLength() -> Int { return UserPolicy.maxPrecautionLength } - - // MARK: - Repository +} + +// MARK: - Repository +extension DefaultUserUseCase { public func postSocialLogin(_ reqDTO: PostSocialLoginReqDTO) async throws -> PostSocialLoginResDTO { return try await userRepostiory.postSocialLogin(reqDTO) } - + public func postSignUp(_ reqDTO: PostSignUpReqDTO, profileImage: Data?) async throws -> PostSignUpResDTO { return try await userRepostiory.postSignUp(reqDTO, profileImage: profileImage) } - } +