From afe4beba48dea829d18e0917425425b0eb19add3 Mon Sep 17 00:00:00 2001 From: s Date: Mon, 18 Mar 2024 22:42:15 +0900 Subject: [PATCH 01/45] =?UTF-8?q?BURN=20::=20CsrfAPI=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 더 이상 사용되지 않아서 삭제합니다. --- .../MGNetworks/Sources/API/Auth/CsrfAPI.swift | 34 ------------------- 1 file changed, 34 deletions(-) delete mode 100644 Projects/Modules/MGNetworks/Sources/API/Auth/CsrfAPI.swift diff --git a/Projects/Modules/MGNetworks/Sources/API/Auth/CsrfAPI.swift b/Projects/Modules/MGNetworks/Sources/API/Auth/CsrfAPI.swift deleted file mode 100644 index 8ef9ab27..00000000 --- a/Projects/Modules/MGNetworks/Sources/API/Auth/CsrfAPI.swift +++ /dev/null @@ -1,34 +0,0 @@ -import Foundation - -import Alamofire -import Moya - -import Core - -public enum CsrfAPI { - case getCSRFToken -} - -extension CsrfAPI: BaseAPI { - - public static var apiType: APIType = .CSRFToken - - public var path: String { - switch self { - case .getCSRFToken: - return "/csrf" - } - } - - public var method: Moya.Method { - return .get - } - - public var task: Task { - return .requestPlain - } - - public var headers: [String : String]? { - return nil - } -} From 77cfa590c8a2e533ca7da8d218d5850a77e1511e Mon Sep 17 00:00:00 2001 From: s Date: Mon, 18 Mar 2024 22:43:26 +0900 Subject: [PATCH 02/45] =?UTF-8?q?ENCHANT=20::=20[IOS-47]=20AppleAPI=20?= =?UTF-8?q?=EC=84=B1=EB=8A=A5=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 코드 스타일 통일 및 accessToken 코드 추가 --- .../Sources/API/Auth/AppleAPI.swift | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/Projects/Modules/MGNetworks/Sources/API/Auth/AppleAPI.swift b/Projects/Modules/MGNetworks/Sources/API/Auth/AppleAPI.swift index 4aa2c63b..609674d7 100644 --- a/Projects/Modules/MGNetworks/Sources/API/Auth/AppleAPI.swift +++ b/Projects/Modules/MGNetworks/Sources/API/Auth/AppleAPI.swift @@ -1,15 +1,13 @@ import Foundation -import Alamofire import Moya import Core - -import TokenManager +import Alamofire public enum AppleAPI { - case appleLogin case appleSignup(nickname: String, accessToken: String) - case appleRecovery + case appleLogin(accessToken: String) + case appleRecovery(accessToken: String) } extension AppleAPI: BaseAPI { @@ -18,10 +16,10 @@ extension AppleAPI: BaseAPI { public var path: String { switch self { - case .appleLogin: - return "/login" case .appleSignup: return "/signup" + case .appleLogin: + return "/login" case .appleRecovery: return "/recovery" } @@ -29,10 +27,10 @@ extension AppleAPI: BaseAPI { public var method: Moya.Method { switch self { - case .appleLogin: - return .get case .appleSignup: return .post + case .appleLogin: + return .get case .appleRecovery: return .put } @@ -46,8 +44,16 @@ extension AppleAPI: BaseAPI { "access_token": accessToken ] return .requestParameters(parameters: parameters, encoding: JSONEncoding.default) - case .appleLogin, .appleRecovery: - return .requestPlain + case let .appleLogin(accessToken): + let parameters: [String: Any] = [ + "access_token": accessToken + ] + return .requestParameters(parameters: parameters, encoding: JSONEncoding.default) + case let .appleRecovery(accessToken): + let parameters: [String: Any] = [ + "access_token": accessToken + ] + return .requestParameters(parameters: parameters, encoding: JSONEncoding.default) } } From c1a613a312e0721b6e4a7d009a34dac36a8698cd Mon Sep 17 00:00:00 2001 From: s Date: Mon, 18 Mar 2024 22:43:56 +0900 Subject: [PATCH 03/45] =?UTF-8?q?ENCHANT=20::=20[IOS-47]=08GoogleAPI=20?= =?UTF-8?q?=EC=84=B1=EB=8A=A5=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 코드 스타일 통일 및 accessToken 코드 추가 --- .../Sources/API/Auth/GoogleAPI.swift | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/Projects/Modules/MGNetworks/Sources/API/Auth/GoogleAPI.swift b/Projects/Modules/MGNetworks/Sources/API/Auth/GoogleAPI.swift index cdb1103e..39d902e2 100644 --- a/Projects/Modules/MGNetworks/Sources/API/Auth/GoogleAPI.swift +++ b/Projects/Modules/MGNetworks/Sources/API/Auth/GoogleAPI.swift @@ -1,15 +1,13 @@ import Foundation -import Alamofire import Moya import Core - -import TokenManager +import Alamofire public enum GoogleAPI { - case googleLogin case googleSignup(nickname: String, accessToken: String) - case googleRecovery + case googleLogin(accessToken: String) + case googleRecovery(accessToken: String) } extension GoogleAPI: BaseAPI { @@ -18,10 +16,10 @@ extension GoogleAPI: BaseAPI { public var path: String { switch self { - case .googleLogin: - return "/login" case .googleSignup: return "/signup" + case .googleLogin: + return "/login" case .googleRecovery: return "/recovery" } @@ -29,10 +27,10 @@ extension GoogleAPI: BaseAPI { public var method: Moya.Method { switch self { - case .googleLogin: - return .get case .googleSignup: return .post + case .googleLogin: + return .get case .googleRecovery: return .put } @@ -46,8 +44,16 @@ extension GoogleAPI: BaseAPI { "access_token": accessToken ] return .requestParameters(parameters: parameters, encoding: JSONEncoding.default) - case .googleLogin, .googleRecovery: - return .requestPlain + case let .googleLogin(accessToken): + let parameters: [String: Any] = [ + "access_token": accessToken + ] + return .requestParameters(parameters: parameters, encoding: JSONEncoding.default) + case let .googleRecovery(accessToken): + let parameters: [String: Any] = [ + "access_token": accessToken + ] + return .requestParameters(parameters: parameters, encoding: JSONEncoding.default) } } From e4bd709901bc505a402b68c166965ec1bd416cd6 Mon Sep 17 00:00:00 2001 From: s Date: Mon, 18 Mar 2024 22:44:31 +0900 Subject: [PATCH 04/45] =?UTF-8?q?ENCHANT=20::=20[IOS-47]=20KakaoAPI=20?= =?UTF-8?q?=EC=84=B1=EB=8A=A5=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 코드 스타일 통일 및 accessToken 코드 추가 --- .../Sources/API/Auth/KakaoAPI.swift | 50 ++++++++----------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/Projects/Modules/MGNetworks/Sources/API/Auth/KakaoAPI.swift b/Projects/Modules/MGNetworks/Sources/API/Auth/KakaoAPI.swift index 0287c819..3711f40f 100644 --- a/Projects/Modules/MGNetworks/Sources/API/Auth/KakaoAPI.swift +++ b/Projects/Modules/MGNetworks/Sources/API/Auth/KakaoAPI.swift @@ -1,43 +1,41 @@ import Foundation -import Alamofire import Moya import Core - -import TokenManager +import Alamofire public enum KakaoAPI { - case kakaoLogin case kakaoSignup(nickname: String, accessToken: String) - case kakaoRecovery + case kakaoLogin(accessToken: String) + case kakaoRecovery(accessToken: String) } extension KakaoAPI: BaseAPI { - + public static var apiType: APIType = .kakao public var path: String { switch self { - case .kakaoLogin: - return "/login" case .kakaoSignup: return "/signup" + case .kakaoLogin: + return "/login" case .kakaoRecovery: return "/recovery" } } - + public var method: Moya.Method { switch self { - case .kakaoLogin: - return .get case .kakaoSignup: return .post + case .kakaoLogin: + return .get case .kakaoRecovery: return .put } } - + public var task: Moya.Task { switch self { case let .kakaoSignup(nickname, accessToken): @@ -46,24 +44,20 @@ extension KakaoAPI: BaseAPI { "access_token": accessToken ] return .requestParameters(parameters: parameters, encoding: JSONEncoding.default) - case .kakaoLogin, .kakaoRecovery: - return .requestPlain + case let .kakaoLogin(accessToken): + let parameters: [String: Any] = [ + "access_token": accessToken + ] + return .requestParameters(parameters: parameters, encoding: JSONEncoding.default) + case let .kakaoRecovery(accessToken): + let parameters: [String: Any] = [ + "access_token": accessToken + ] + return .requestParameters(parameters: parameters, encoding: JSONEncoding.default) } } - + public var headers: [String : String]? { - switch self { - case .kakaoSignup: - if let csrfToken = TokenManagerImpl().get(key: .CSRFToken) { - return [ - "X-XSRF-TOKEN": csrfToken - ] - } else { - return nil - } - case .kakaoLogin, .kakaoRecovery: - return nil - } + return nil } - } From c51d7a8e5cb78c1ffabce5664a42eacd496987a2 Mon Sep 17 00:00:00 2001 From: s Date: Mon, 18 Mar 2024 23:22:11 +0900 Subject: [PATCH 05/45] =?UTF-8?q?FEAT=20::=20[IOS-47]=20API=20=ED=8C=8C?= =?UTF-8?q?=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/Modules/MGNetworks/Sources/API/Auth/AppleAPI.swift | 6 +++--- .../Modules/MGNetworks/Sources/API/Auth/GoogleAPI.swift | 6 +++--- Projects/Modules/MGNetworks/Sources/API/Auth/KakaoAPI.swift | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Projects/Modules/MGNetworks/Sources/API/Auth/AppleAPI.swift b/Projects/Modules/MGNetworks/Sources/API/Auth/AppleAPI.swift index 609674d7..38df6ded 100644 --- a/Projects/Modules/MGNetworks/Sources/API/Auth/AppleAPI.swift +++ b/Projects/Modules/MGNetworks/Sources/API/Auth/AppleAPI.swift @@ -43,17 +43,17 @@ extension AppleAPI: BaseAPI { "nickname": nickname, "access_token": accessToken ] - return .requestParameters(parameters: parameters, encoding: JSONEncoding.default) + return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) case let .appleLogin(accessToken): let parameters: [String: Any] = [ "access_token": accessToken ] - return .requestParameters(parameters: parameters, encoding: JSONEncoding.default) + return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) case let .appleRecovery(accessToken): let parameters: [String: Any] = [ "access_token": accessToken ] - return .requestParameters(parameters: parameters, encoding: JSONEncoding.default) + return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) } } diff --git a/Projects/Modules/MGNetworks/Sources/API/Auth/GoogleAPI.swift b/Projects/Modules/MGNetworks/Sources/API/Auth/GoogleAPI.swift index 39d902e2..3941590d 100644 --- a/Projects/Modules/MGNetworks/Sources/API/Auth/GoogleAPI.swift +++ b/Projects/Modules/MGNetworks/Sources/API/Auth/GoogleAPI.swift @@ -43,17 +43,17 @@ extension GoogleAPI: BaseAPI { "nickname": nickname, "access_token": accessToken ] - return .requestParameters(parameters: parameters, encoding: JSONEncoding.default) + return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) case let .googleLogin(accessToken): let parameters: [String: Any] = [ "access_token": accessToken ] - return .requestParameters(parameters: parameters, encoding: JSONEncoding.default) + return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) case let .googleRecovery(accessToken): let parameters: [String: Any] = [ "access_token": accessToken ] - return .requestParameters(parameters: parameters, encoding: JSONEncoding.default) + return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) } } diff --git a/Projects/Modules/MGNetworks/Sources/API/Auth/KakaoAPI.swift b/Projects/Modules/MGNetworks/Sources/API/Auth/KakaoAPI.swift index 3711f40f..998b8e2a 100644 --- a/Projects/Modules/MGNetworks/Sources/API/Auth/KakaoAPI.swift +++ b/Projects/Modules/MGNetworks/Sources/API/Auth/KakaoAPI.swift @@ -43,17 +43,17 @@ extension KakaoAPI: BaseAPI { "nickname": nickname, "access_token": accessToken ] - return .requestParameters(parameters: parameters, encoding: JSONEncoding.default) + return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) case let .kakaoLogin(accessToken): let parameters: [String: Any] = [ "access_token": accessToken ] - return .requestParameters(parameters: parameters, encoding: JSONEncoding.default) + return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) case let .kakaoRecovery(accessToken): let parameters: [String: Any] = [ "access_token": accessToken ] - return .requestParameters(parameters: parameters, encoding: JSONEncoding.default) + return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) } } From 63100c1bc962c37ab95dd338ab51775ee356df65 Mon Sep 17 00:00:00 2001 From: s Date: Fri, 22 Mar 2024 21:37:44 +0900 Subject: [PATCH 06/45] =?UTF-8?q?FIX=20::=20[IOS-47]=20auth=20service=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Service/AuthService.swift | 172 ++++++++++-------- 1 file changed, 96 insertions(+), 76 deletions(-) diff --git a/Projects/Modules/MGNetworks/Sources/Service/AuthService.swift b/Projects/Modules/MGNetworks/Sources/Service/AuthService.swift index 2a58f671..b6eb030a 100644 --- a/Projects/Modules/MGNetworks/Sources/Service/AuthService.swift +++ b/Projects/Modules/MGNetworks/Sources/Service/AuthService.swift @@ -1,5 +1,4 @@ import UIKit -import AuthenticationServices import RxSwift import RxCocoa @@ -7,57 +6,55 @@ import RxCocoa import RxMoya import Moya -import Domain import DSKit import TokenManager +import Domain import KakaoSDKUser +import AuthenticationServices public class AuthService: NSObject { - let provider = MoyaProvider() let kakaoProvider = MoyaProvider() let googleProvider = MoyaProvider() let appleProvider = MoyaProvider() - - public func googleSignup(nickname: String, accessToken: String) -> Single { - return googleProvider.rx.request(.googleSignup(nickname: nickname, accessToken: accessToken)) - .mapString() - } - - public func googleLogin() -> Single { - return googleProvider.rx.request(.googleLogin) - .mapString() - } - -// public func googleTokenState() -> Single { -// return Single.create { [weak self] single in - -// } -// } - + private let keychainAuthorization = KeychainType.authorizationToken private let appleSignupSubject = PublishSubject() - - public func requestToken() -> Single { - return Single.just(true) - } - - public func kakaoLogin() -> Single { - return kakaoProvider.rx.request(.kakaoLogin) - .mapString() + + public func oauthSingup(nickname: String, accessToken: String, oauth: OauthType) -> Observable { + switch oauth { + case .google: + return googleSignup(nickname: nickname, accessToken: accessToken) + case .kakao: + return kakaoSignup(nickname: nickname, accessToken: accessToken) + case .apple: + return appleSignup(nickname: nickname, accessToken: accessToken) + } } - - public func kakaoSignup(nickname: String, accessToken: String) -> Single { - return kakaoProvider.rx.request(.kakaoSignup(nickname: nickname, accessToken: accessToken)) - .mapString() + + public func oauthLogin(accessToken: String, oauth: OauthType) -> Observable { + switch oauth { + case .google: + return googleLogin(accessToken: accessToken) + case .kakao: + return kakaoLogin(accessToken: accessToken) + case .apple: + return appleLogin(accessToken: accessToken) + } } - - public func kakaoRecovery() -> Single { - return kakaoProvider.rx.request(.kakaoRecovery) - .mapString() + + public func oauthRecovery(accessToken: String, oauth: OauthType) -> Observable { + switch oauth { + case .google: + return googleRecovery(accessToken: accessToken) + case .kakao: + return kakaoRecovery(accessToken: accessToken) + case .apple: + return appleRecovery(accessToken: accessToken) + } } - + public func kakaoTokenState() -> Single { return Single.create { [weak self] single in if UserApi.isKakaoTalkLoginAvailable() { @@ -82,57 +79,30 @@ public class AuthService: NSObject { return Disposables.create() } } - - public func getCSRFToken() -> Single { - return provider.rx.request(.getCSRFToken) - .flatMap { response -> Single in - if let setCookieHeader = response.response?.allHeaderFields["Set-Cookie"] as? String { - let cookies = setCookieHeader.components(separatedBy: ", ") - for cookie in cookies { - if cookie.hasPrefix("XSRF-TOKEN") { - let token = cookie.components(separatedBy: "=")[1].components(separatedBy: ";")[0] - return Single.just(token) - } - } - } - return Single.error(AuthError.tokenNotFound) - } + + public func requestToken() -> Single { + return Single.just(true) } - + public func requestIntroData() -> Single { return Single.just(IntroModel(image: DSKitAsset.Assets.airSqt.image, mainTitle: "이제 헬창이 되어보세요!", subTitle: "저희의 좋은 서비스를 통해 즐거운 생활을\n즐겨보세요!")) } - - public func appleLogin() -> Single { - return appleProvider.rx.request(.appleLogin) - .mapString() - } - - public func appleSignup(nickname: String, accessToken: String) -> Single { - return appleProvider.rx.request(.appleSignup(nickname: nickname, accessToken: accessToken)) - .mapString() - } - - public func appleRecovery() -> Single { - return appleProvider.rx.request(.appleRecovery) - .mapString() - } - - public func appleSignup() -> Single { + + public func appleButtonTap() -> Single { let appleProvider = ASAuthorizationAppleIDProvider() let request = appleProvider.createRequest() request.requestedScopes = [.fullName, .email] - + let controller = ASAuthorizationController(authorizationRequests: [request]) controller.performRequests() - + controller.delegate = self - + return appleSignupSubject.take(1).asSingle() } - + public override init() { - + } } @@ -146,7 +116,7 @@ extension AuthService: ASAuthorizationControllerDelegate { let tokenString = String(data: identityToken, encoding: .utf8) { appleSignupSubject.onNext(tokenString) appleSignupSubject.onCompleted() - TokenManagerImpl().save(token: tokenString, with: keychainAuthorization) + oauthLogin(accessToken: tokenString, oauth: .apple) } default: break @@ -157,3 +127,53 @@ extension AuthService: ASAuthorizationControllerDelegate { appleSignupSubject.onError(error) } } + +private extension AuthService { + func googleSignup(nickname: String, accessToken: String) -> Observable { + return googleProvider.rx.request(.googleSignup(nickname: nickname, accessToken: accessToken)).mapString().asObservable() + } + + func googleLogin(accessToken: String) -> Observable { + return googleProvider.rx.request(.googleLogin(accessToken: accessToken)).mapString().asObservable() + } + + func googleRecovery(accessToken: String) -> Observable { + return googleProvider.rx.request(.googleRecovery(accessToken: accessToken)) + .mapString().asObservable() + } + + func kakaoSignup(nickname: String, accessToken: String) -> Observable { + return kakaoProvider.rx.request(.kakaoSignup(nickname: nickname, accessToken: accessToken)) + .mapString().asObservable() + } + + func kakaoLogin(accessToken: String) -> Observable { + return kakaoProvider.rx.request(.kakaoLogin(accessToken: accessToken)).mapString().asObservable() + } + + func kakaoRecovery(accessToken: String) -> Observable { + return kakaoProvider.rx.request(.kakaoRecovery(accessToken: accessToken)) + .mapString().asObservable() + } + + func appleSignup(nickname: String, accessToken: String) -> Observable { + return appleProvider.rx.request(.appleSignup(nickname: nickname, accessToken: accessToken)) + .mapString().asObservable() + } + + func appleLogin(accessToken: String) -> Observable { + return appleProvider.rx.request(.appleLogin(accessToken: accessToken)) + .mapString().asObservable() + } + + func appleRecovery(accessToken: String) -> Observable { + return appleProvider.rx.request(.appleRecovery(accessToken: accessToken)) + .mapString().asObservable() + } + + // public func appleTokenState() -> Single { + // return Single.create { [weak self] single in + + // } + // } +} From 11c0a9c152fa7c931d63da1ac30b917cf7012678 Mon Sep 17 00:00:00 2001 From: s Date: Sat, 23 Mar 2024 09:34:48 +0900 Subject: [PATCH 07/45] ADD :: [IOS-47] keychainType MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refreshToken case 추가 --- Projects/Modules/TokenManager/Sources/KeychainType.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Projects/Modules/TokenManager/Sources/KeychainType.swift b/Projects/Modules/TokenManager/Sources/KeychainType.swift index eccf99ff..a05d3baa 100644 --- a/Projects/Modules/TokenManager/Sources/KeychainType.swift +++ b/Projects/Modules/TokenManager/Sources/KeychainType.swift @@ -2,6 +2,7 @@ import Foundation public enum KeychainType: String { case authorizationToken + case refreshToken case CSRFToken case test } From 0c061de1d3f0116ea429f30c74a54c57f1e6ff2b Mon Sep 17 00:00:00 2001 From: s Date: Sat, 23 Mar 2024 10:14:25 +0900 Subject: [PATCH 08/45] ADD :: [IOS-47] AuthUseCase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit apple login 구현 --- .../Domain/Sources/UseCase/AuthUseCase.swift | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/Projects/Domain/Sources/UseCase/AuthUseCase.swift b/Projects/Domain/Sources/UseCase/AuthUseCase.swift index a86a8915..8f631b8e 100644 --- a/Projects/Domain/Sources/UseCase/AuthUseCase.swift +++ b/Projects/Domain/Sources/UseCase/AuthUseCase.swift @@ -6,14 +6,14 @@ import RxCocoa import Core import Moya +import MGLogger import TokenManager import AuthenticationServices public protocol AuthUseCase { func kakaoButtonTap() - func getCSRFToken() -> Single func getIntroData() - func appleButtonTap() -> Single + func appleButtonTap() // func appleNickNameButtonTap() -> Single var appleSignupResult: PublishSubject { get } var introData: PublishSubject { get } @@ -34,21 +34,34 @@ public class DefaultAuthUseCase { } extension DefaultAuthUseCase: AuthUseCase { - - - public func appleButtonTap() -> Single { - authRepository.appleSignup() + + public func appleButtonTap() { + authRepository.appleButtonTap() .subscribe( - onSuccess: { [weak self] token in - self?.appleSignupResult.onNext(token) + onSuccess: { [self] token in + appleSignupResult.onNext(token) + TokenManagerImpl().save(token: token, with: KeychainType.authorizationToken) + authRepository.oauthLogin(accessToken: token, oauth: .apple) + .subscribe(onNext: { element in + MGLogger.debug("\(element)") + AuthStepper.shared.steps.accept(MGStep.authCompleteIsRequired) + }, onError: { error in + MGLogger.debug("appleLogin : \(error)") + self.authRepository.oauthRecovery(accessToken: token, oauth: .apple) + .subscribe(onNext: { element in + MGLogger.debug("\(element)") + }, onError: { error in + MGLogger.debug("\(error)") + AuthStepper.shared.steps.accept(MGStep.authAgreeIsRequired) + }).disposed(by: self.disposeBag) + } + ).disposed(by: disposeBag) }, onFailure: { [weak self] error in self?.appleSignupResult.onError(error) } ) .disposed(by: disposeBag) - - return appleSignupResult.take(1).asSingle() } public func getIntroData() { @@ -62,10 +75,6 @@ extension DefaultAuthUseCase: AuthUseCase { .disposed(by: disposeBag) } - public func getCSRFToken() -> Single { - return authRepository.getCSRFToken() - } - public func kakaoButtonTap() { return authRepository.kakaoToken() .subscribe(onSuccess: { _ in From bea05aa611d99fabb65dfa6acc6ef634319e37eb Mon Sep 17 00:00:00 2001 From: s Date: Sat, 23 Mar 2024 10:15:01 +0900 Subject: [PATCH 09/45] =?UTF-8?q?FIX=20::=20[IOS-47]=20=EB=B0=98=ED=99=98?= =?UTF-8?q?=20=EA=B0=92=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DTO 적용 --- .../Sources/Service/AuthService.swift | 59 ++++++++++++------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/Projects/Modules/MGNetworks/Sources/Service/AuthService.swift b/Projects/Modules/MGNetworks/Sources/Service/AuthService.swift index b6eb030a..55159788 100644 --- a/Projects/Modules/MGNetworks/Sources/Service/AuthService.swift +++ b/Projects/Modules/MGNetworks/Sources/Service/AuthService.swift @@ -22,7 +22,7 @@ public class AuthService: NSObject { private let keychainAuthorization = KeychainType.authorizationToken private let appleSignupSubject = PublishSubject() - public func oauthSingup(nickname: String, accessToken: String, oauth: OauthType) -> Observable { + public func oauthSingup(nickname: String, accessToken: String, oauth: OauthType) -> Observable { switch oauth { case .google: return googleSignup(nickname: nickname, accessToken: accessToken) @@ -33,7 +33,18 @@ public class AuthService: NSObject { } } - public func oauthLogin(accessToken: String, oauth: OauthType) -> Observable { +// public func oauthLogin(accessToken: String, oauth: OauthType) -> Observable { +// switch oauth { +// case .google: +// return googleLogin(accessToken: accessToken) +// case .kakao: +// return kakaoLogin(accessToken: accessToken) +// case .apple: +// return appleLogin(accessToken: accessToken) +// } +// } + + public func oauthLogin(accessToken: String, oauth: OauthType) -> Observable { switch oauth { case .google: return googleLogin(accessToken: accessToken) @@ -44,7 +55,7 @@ public class AuthService: NSObject { } } - public func oauthRecovery(accessToken: String, oauth: OauthType) -> Observable { + public func oauthRecovery(accessToken: String, oauth: OauthType) -> Observable { switch oauth { case .google: return googleRecovery(accessToken: accessToken) @@ -129,46 +140,52 @@ extension AuthService: ASAuthorizationControllerDelegate { } private extension AuthService { - func googleSignup(nickname: String, accessToken: String) -> Observable { - return googleProvider.rx.request(.googleSignup(nickname: nickname, accessToken: accessToken)).mapString().asObservable() + func googleSignup(nickname: String, accessToken: String) -> Observable { + return googleProvider.rx.request(.googleSignup(nickname: nickname, accessToken: accessToken)).map(SignupResponseDTO.self).asObservable() } - func googleLogin(accessToken: String) -> Observable { - return googleProvider.rx.request(.googleLogin(accessToken: accessToken)).mapString().asObservable() + func googleLogin(accessToken: String) -> Observable { + return googleProvider.rx.request(.googleLogin(accessToken: accessToken)) + .map(LoginResponseDTO.self) + .asObservable() } - func googleRecovery(accessToken: String) -> Observable { + func googleRecovery(accessToken: String) -> Observable { return googleProvider.rx.request(.googleRecovery(accessToken: accessToken)) - .mapString().asObservable() + .map(RecoveryResponseDTO.self) + .asObservable() } - func kakaoSignup(nickname: String, accessToken: String) -> Observable { + func kakaoSignup(nickname: String, accessToken: String) -> Observable { return kakaoProvider.rx.request(.kakaoSignup(nickname: nickname, accessToken: accessToken)) - .mapString().asObservable() + .map(SignupResponseDTO.self).asObservable() } - func kakaoLogin(accessToken: String) -> Observable { - return kakaoProvider.rx.request(.kakaoLogin(accessToken: accessToken)).mapString().asObservable() + func kakaoLogin(accessToken: String) -> Observable { + return kakaoProvider.rx.request(.kakaoLogin(accessToken: accessToken)) + .map(LoginResponseDTO.self) + .asObservable() } - func kakaoRecovery(accessToken: String) -> Observable { + func kakaoRecovery(accessToken: String) -> Observable { return kakaoProvider.rx.request(.kakaoRecovery(accessToken: accessToken)) - .mapString().asObservable() + .map(RecoveryResponseDTO.self).asObservable() } - func appleSignup(nickname: String, accessToken: String) -> Observable { + func appleSignup(nickname: String, accessToken: String) -> Observable { return appleProvider.rx.request(.appleSignup(nickname: nickname, accessToken: accessToken)) - .mapString().asObservable() + .map(SignupResponseDTO.self).asObservable() } - func appleLogin(accessToken: String) -> Observable { + func appleLogin(accessToken: String) -> Observable { return appleProvider.rx.request(.appleLogin(accessToken: accessToken)) - .mapString().asObservable() + .map(LoginResponseDTO.self) + .asObservable() } - func appleRecovery(accessToken: String) -> Observable { + func appleRecovery(accessToken: String) -> Observable { return appleProvider.rx.request(.appleRecovery(accessToken: accessToken)) - .mapString().asObservable() + .map(RecoveryResponseDTO.self).asObservable() } // public func appleTokenState() -> Single { From b6778ef801c5c7d08b5cf9203eafa5dfd668581b Mon Sep 17 00:00:00 2001 From: s Date: Sat, 23 Mar 2024 10:15:49 +0900 Subject: [PATCH 10/45] ADD :: [IOS-47] Auth Repository Interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DTO 반환 값 추가 --- .../AuthRepositoryInterface.swift | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/Projects/Domain/Sources/RepositoryInterface/AuthRepositoryInterface.swift b/Projects/Domain/Sources/RepositoryInterface/AuthRepositoryInterface.swift index eafcc0ad..ae0d0801 100644 --- a/Projects/Domain/Sources/RepositoryInterface/AuthRepositoryInterface.swift +++ b/Projects/Domain/Sources/RepositoryInterface/AuthRepositoryInterface.swift @@ -3,10 +3,33 @@ import UIKit import RxSwift import RxCocoa +public enum OauthType { + case google + case kakao + case apple +} + +public struct LoginResponseDTO: Decodable { + public let status: Int + public let accessToken: String + public let refreshToken: String +} + +public struct SignupResponseDTO: Decodable { + public let status: Int +} + +public struct RecoveryResponseDTO: Decodable { + public let status: Int +} + public protocol AuthRepositoryInterface { + func googleToken() -> Single func kakaoToken() -> Single - func getCSRFToken() -> Single + func appleToken() -> Single + func appleButtonTap() -> Single + func oauthSignup(nickname: String, accessToken: String, oauth: OauthType) -> Observable + func oauthLogin(accessToken: String, oauth: OauthType) -> Observable + func oauthRecovery(accessToken: String, oauth: OauthType) -> Observable func getIntroData() -> Single - func appleSignup() -> Single - func appleSingup(nickname: String, accessToken: String) -> Single } From 374dfea4d6ce3045e2e44ac5b9904a96a2823cbf Mon Sep 17 00:00:00 2001 From: s Date: Sat, 23 Mar 2024 10:16:16 +0900 Subject: [PATCH 11/45] FIX :: [IOS-47] Aut Repository MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DTO 적용 --- .../Sources/Repository/AuthRepository.swift | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/Projects/Data/Sources/Repository/AuthRepository.swift b/Projects/Data/Sources/Repository/AuthRepository.swift index ee32e741..d30272c4 100644 --- a/Projects/Data/Sources/Repository/AuthRepository.swift +++ b/Projects/Data/Sources/Repository/AuthRepository.swift @@ -1,4 +1,5 @@ import UIKit +import MGNetworks import RxSwift import RxCocoa @@ -7,32 +8,48 @@ import Moya import RxMoya import Domain -import MGNetworks public class AuthRepository: AuthRepositoryInterface { + private let networkService: AuthService - public init(networkService: AuthService) { - self.networkService = networkService + public func googleToken() -> Single { + return networkService.kakaoTokenState() } public func kakaoToken() -> Single { - networkService.kakaoTokenState() + return networkService.kakaoTokenState() } - - public func getCSRFToken() -> Single { - return networkService.getCSRFToken() + + public func appleToken() -> Single { + return networkService.kakaoTokenState() + } + + public func oauthSignup(nickname: String, accessToken: String, oauth: OauthType) -> Observable { + return networkService.oauthSingup(nickname: nickname, accessToken: accessToken, oauth: oauth) + } + + public func oauthLogin(accessToken: String, oauth: OauthType) -> Observable { + return networkService.oauthLogin(accessToken: accessToken, oauth: oauth) } + public func oauthRecovery(accessToken: String, oauth: OauthType) -> Observable { + return networkService.oauthRecovery(accessToken: accessToken, oauth: oauth) + } + public func getIntroData() -> Single { return networkService.requestIntroData() } - public func appleSignup() -> RxSwift.Single { - return networkService.appleSignup() + public func appleButtonTap() -> Single { + return networkService.appleButtonTap() } - public func appleSingup(nickname: String, accessToken: String) -> Single { - return networkService.appleSignup(nickname: nickname, accessToken: accessToken) + public init(networkService: AuthService) { + self.networkService = networkService } + +// public func appleSingup(nickname: String, accessToken: String) -> Single { +// return networkService.appleSignup(nickname: nickname, accessToken: accessToken) +// } } From ec4ecdfd353f98f89ef7b8c8834e43a3e6cc2a8f Mon Sep 17 00:00:00 2001 From: s Date: Sat, 23 Mar 2024 20:44:35 +0900 Subject: [PATCH 12/45] =?UTF-8?q?FIX=20::=20[IOS-47]=20=20bind=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SignupScene/VC/AgreeViewController.swift | 149 +++++++++++++++--- 1 file changed, 127 insertions(+), 22 deletions(-) diff --git a/Projects/Features/AuthFeature/Sources/SignupScene/VC/AgreeViewController.swift b/Projects/Features/AuthFeature/Sources/SignupScene/VC/AgreeViewController.swift index e06b7702..4b3b3b20 100644 --- a/Projects/Features/AuthFeature/Sources/SignupScene/VC/AgreeViewController.swift +++ b/Projects/Features/AuthFeature/Sources/SignupScene/VC/AgreeViewController.swift @@ -21,6 +21,8 @@ public class AgreeViewController: BaseViewController, UIGestureR private var naviBar = AuthNavigationBarBar() + private let containerView = BaseView() + private let agreeLabel = MGLabel( text: "약관동의", font: UIFont.Pretendard.titleLarge @@ -32,12 +34,22 @@ public class AgreeViewController: BaseViewController, UIGestureR textColor: DSKitAsset.Colors.gray600.color ) - private let agreeTermsView = MGAgreeView( - firstAgreeText: .privacyAgreeText, - secondAgreeText: .termsAgreeText, - thirdAgreeText: .ageAgreeText, - fourthAgreeText: .marketingAgreeText - ) + private let decorateLine1 = MGLine() + private let allAgreeButton = MGAgreeButton(type: .allAgreeText) + private let decorateLine2 = MGLine() + + private let firstAgreeButton = MGAgreeButton(type: .privacyAgreeText) + private let secondAgreeButton = MGAgreeButton(type: .termsAgreeText) + private let thirdAgreeButton = MGAgreeButton(type: .ageAgreeText) + private let fourthAgreeButton = MGAgreeButton(type: .marketingAgreeText, chooseType: true) + + // private let readMore = MGButton( + // titleText: "자세히 보기", + // font: UIFont.Pretendard.labelSmall, + // textColor: DSKitAsset.Colors.gray300.color + // ).then { + // $0.isHidden = false + // } private var checkButton = MGCheckButton(text: "확인") @@ -53,7 +65,7 @@ public class AgreeViewController: BaseViewController, UIGestureR } public override func layout() { - view.addSubviews([naviBar, agreeLabel, textInformation, agreeTermsView, checkButton]) + view.addSubviews([naviBar, agreeLabel, textInformation, containerView, checkButton]) naviBar.snp.makeConstraints { $0.leading.top.trailing.equalTo(view.safeAreaLayoutGuide) @@ -71,9 +83,10 @@ public class AgreeViewController: BaseViewController, UIGestureR $0.width.equalTo(287.0) } - agreeTermsView.snp.makeConstraints { + containerView.snp.makeConstraints { $0.top.equalTo(textInformation.snp.bottom).offset(40.0) - $0.leading.equalToSuperview().offset(20.0) + $0.height.equalTo(284.0) + $0.leading.trailing.equalToSuperview().inset(20.0) } checkButton.snp.makeConstraints { @@ -82,6 +95,54 @@ public class AgreeViewController: BaseViewController, UIGestureR $0.trailing.equalToSuperview().offset(-20.0) $0.height.equalTo(58.0) } + + containerView.addSubviews([decorateLine1, + allAgreeButton, + decorateLine2, + firstAgreeButton, + secondAgreeButton, + thirdAgreeButton, + fourthAgreeButton]) + + decorateLine1.snp.makeConstraints { + $0.top.equalToSuperview() + $0.centerX.equalToSuperview() + } + + allAgreeButton.snp.makeConstraints { + $0.top.equalTo(decorateLine1.snp.bottom).offset(12.0) + $0.leading.trailing.equalToSuperview() + $0.height.equalTo(44.0) + } + + decorateLine2.snp.makeConstraints { + $0.top.equalTo(allAgreeButton.snp.bottom).offset(12.0) + $0.centerX.equalToSuperview() + } + + firstAgreeButton.snp.makeConstraints { + $0.top.equalTo(decorateLine2.snp.bottom).offset(8.0) + $0.leading.trailing.equalToSuperview() + $0.height.equalTo(44.0) + } + + secondAgreeButton.snp.makeConstraints { + $0.top.equalTo(firstAgreeButton.snp.bottom).offset(8.0) + $0.leading.trailing.equalToSuperview() + $0.height.equalTo(44.0) + } + + thirdAgreeButton.snp.makeConstraints { + $0.top.equalTo(secondAgreeButton.snp.bottom).offset(8.0) + $0.leading.trailing.equalToSuperview() + $0.height.equalTo(44.0) + } + + fourthAgreeButton.snp.makeConstraints { + $0.top.equalTo(thirdAgreeButton.snp.bottom).offset(8.0) + $0.leading.trailing.equalToSuperview() + $0.height.equalTo(44.0) + } } public override func bindViewModel() { @@ -94,11 +155,11 @@ public class AgreeViewController: BaseViewController, UIGestureR let input = AgreeViewModel.Input( navButtonTapped: navButtonTapped, - allAgreeButtonTap: agreeTermsView.allAgreeButton.rx.tap.asSignal(), - firstAgreeButtonTap: agreeTermsView.firstAgreeButton.rx.tap.asSignal(), - secondAgreeButtonTap: agreeTermsView.secondAgreeButton.rx.tap.asSignal(), - thirdAgreeButtonTap: agreeTermsView.thirdAgreeButton.rx.tap.asSignal(), - fourthAgreeButtonTap: agreeTermsView.fourthAgreeButton.rx.tap.asSignal(), + allAgreeButtonTap: allAgreeButton.rx.tap.asSignal(), + firstAgreeButtonTap: firstAgreeButton.rx.tap.asSignal(), + secondAgreeButtonTap: secondAgreeButton.rx.tap.asSignal(), + thirdAgreeButtonTap: thirdAgreeButton.rx.tap.asSignal(), + fourthAgreeButtonTap: fourthAgreeButton.rx.tap.asSignal(), nextButtonTap: checkButton.rx.tap.asSignal() ) @@ -106,7 +167,7 @@ public class AgreeViewController: BaseViewController, UIGestureR output.allAgreeButtonClickedMessage .drive(onNext: { [weak self] message in print(message) - self?.agreeTermsView.setAllAgreeButtonState(!(self?.agreeTermsView.allAgreeButtonState ?? false)) + self?.setAllAgreeButtonState(!(self?.allAgreeButtonState ?? false)) }) .disposed(by: disposeBag) @@ -120,7 +181,7 @@ public class AgreeViewController: BaseViewController, UIGestureR agreeButtons.forEach { buttonOutput in buttonOutput .drive(onNext: { [weak self] message in - self?.agreeTermsView.updateAllAgreeButtonState() + self?.updateAllAgreeButtonState() MGLogger.verbose(message) }) .disposed(by: disposeBag) @@ -128,24 +189,24 @@ public class AgreeViewController: BaseViewController, UIGestureR output.firstAgreeButtonClickedMessage .drive(onNext: { message in - self.agreeTermsView.updateAllAgreeButtonState() - _ = self.agreeTermsView.buttonActivationChecked(button: self.checkButton) + self.updateAllAgreeButtonState() + _ = self.buttonActivationChecked(button: self.checkButton) MGLogger.verbose(message) }) .disposed(by: disposeBag) output.secondAgreeButtonClickedMessage .drive(onNext: { message in - self.agreeTermsView.updateAllAgreeButtonState() - _ = self.agreeTermsView.buttonActivationChecked(button: self.checkButton) + self.updateAllAgreeButtonState() + _ = self.buttonActivationChecked(button: self.checkButton) MGLogger.verbose(message) }) .disposed(by: disposeBag) output.thirdAgreeButtonClickedMessage .drive(onNext: { message in - self.agreeTermsView.updateAllAgreeButtonState() - _ = self.agreeTermsView.buttonActivationChecked(button: self.checkButton) + self.updateAllAgreeButtonState() + _ = self.buttonActivationChecked(button: self.checkButton) MGLogger.verbose(message) }) .disposed(by: disposeBag) @@ -164,4 +225,48 @@ public class AgreeViewController: BaseViewController, UIGestureR .disposed(by: disposeBag) }) } + + public var allAgreeButtonState: Bool { + return firstAgreeButton.checked && + secondAgreeButton.checked && + thirdAgreeButton.checked && + fourthAgreeButton.checked + } + + public func setAllAgreeButtonState(_ isEnabled: Bool) { + allAgreeButton.checked = isEnabled + if isEnabled { + firstAgreeButton.buttonYesChecked() + secondAgreeButton.buttonYesChecked() + thirdAgreeButton.buttonYesChecked() + fourthAgreeButton.buttonYesChecked() + updateAllAgreeButtonState() + } else { + firstAgreeButton.buttonNoChecked() + secondAgreeButton.buttonNoChecked() + thirdAgreeButton.buttonNoChecked() + fourthAgreeButton.buttonNoChecked() + updateAllAgreeButtonState() + } + } + + public func updateAllAgreeButtonState() { + if allAgreeButtonState == true { + allAgreeButton.buttonYesChecked() + } else { + allAgreeButton.buttonNoChecked() + } + } + + public func buttonActivationChecked(button: MGCheckButton) -> Bool { + let shouldActivateButton = firstAgreeButton.checked && + secondAgreeButton.checked && + thirdAgreeButton.checked && + !fourthAgreeButton.checked + button.isEnabled = shouldActivateButton + button.backgroundColor = shouldActivateButton ? AuthResourcesService.Colors.blue500 : AuthResourcesService.Colors.gray400 + button.textLabel.textColor = shouldActivateButton ? .white : AuthResourcesService.Colors.gray200 + + return shouldActivateButton + } } From 9c26bb28b4743694076bdf7330b187d4e801d42a Mon Sep 17 00:00:00 2001 From: s Date: Sat, 23 Mar 2024 20:45:30 +0900 Subject: [PATCH 13/45] REFACT :: [IOS-47] agreeViewModel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 리펙토링 --- .../Sources/SignupScene/ViewModel/AgreeViewModel.swift | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Projects/Features/AuthFeature/Sources/SignupScene/ViewModel/AgreeViewModel.swift b/Projects/Features/AuthFeature/Sources/SignupScene/ViewModel/AgreeViewModel.swift index 8ef4c348..d39e6cc2 100644 --- a/Projects/Features/AuthFeature/Sources/SignupScene/ViewModel/AgreeViewModel.swift +++ b/Projects/Features/AuthFeature/Sources/SignupScene/ViewModel/AgreeViewModel.swift @@ -1,8 +1,8 @@ import Foundation -import RxFlow -import RxCocoa import RxSwift +import RxCocoa +import RxFlow import Core import Domain @@ -40,8 +40,6 @@ public class AgreeViewModel: BaseViewModel { self.useCase = useCase } - public var onNextButtonTap: (() -> Void)? - public func transform(_ input: Input, action: (Output) -> Void) -> Output { let allAgreeClickedMessage = input.allAgreeButtonTap.map { "전체 클릭" }.asDriver(onErrorJustReturn: "") From 327fe1576672d80e4e1beac5885824ac17fe0399 Mon Sep 17 00:00:00 2001 From: s Date: Sat, 23 Mar 2024 20:45:49 +0900 Subject: [PATCH 14/45] =?UTF-8?q?BURN=20::=20[IOS-47]=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AuthRepositoryInterface.swift | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/Projects/Domain/Sources/RepositoryInterface/AuthRepositoryInterface.swift b/Projects/Domain/Sources/RepositoryInterface/AuthRepositoryInterface.swift index ae0d0801..ef38e688 100644 --- a/Projects/Domain/Sources/RepositoryInterface/AuthRepositoryInterface.swift +++ b/Projects/Domain/Sources/RepositoryInterface/AuthRepositoryInterface.swift @@ -9,20 +9,6 @@ public enum OauthType { case apple } -public struct LoginResponseDTO: Decodable { - public let status: Int - public let accessToken: String - public let refreshToken: String -} - -public struct SignupResponseDTO: Decodable { - public let status: Int -} - -public struct RecoveryResponseDTO: Decodable { - public let status: Int -} - public protocol AuthRepositoryInterface { func googleToken() -> Single func kakaoToken() -> Single From 21361b335ac2182b3adefc936638b5b36d00071a Mon Sep 17 00:00:00 2001 From: s Date: Sat, 23 Mar 2024 20:46:10 +0900 Subject: [PATCH 15/45] =?UTF-8?q?ADD=20::=20[IOS-47]=20Login=20DTO=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/Domain/Sources/Response/LoginDTO.swift | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 Projects/Domain/Sources/Response/LoginDTO.swift diff --git a/Projects/Domain/Sources/Response/LoginDTO.swift b/Projects/Domain/Sources/Response/LoginDTO.swift new file mode 100644 index 00000000..79f5028a --- /dev/null +++ b/Projects/Domain/Sources/Response/LoginDTO.swift @@ -0,0 +1,15 @@ +import Foundation + +public struct LoginResponseDTO: Decodable { + public let status: Int + public let accessToken: String + public let refreshToken: String +} + +public struct SignupResponseDTO: Decodable { + public let status: Int +} + +public struct RecoveryResponseDTO: Decodable { + public let status: Int +} From 33fe7ae5e1721caed4df14869d4de829fde471e9 Mon Sep 17 00:00:00 2001 From: s Date: Sat, 23 Mar 2024 20:46:29 +0900 Subject: [PATCH 16/45] =?UTF-8?q?ADD=20::=20[MG+Label]=20=ED=8F=B0?= =?UTF-8?q?=ED=8A=B8=20=EB=B3=80=EA=B2=BD=20=ED=95=A8=EC=88=98=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 --- .../DSKit/Sources/Components/MaeumGaGymLabel /MG+Label.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Projects/Modules/DSKit/Sources/Components/MaeumGaGymLabel /MG+Label.swift b/Projects/Modules/DSKit/Sources/Components/MaeumGaGymLabel /MG+Label.swift index edced561..de0d4712 100644 --- a/Projects/Modules/DSKit/Sources/Components/MaeumGaGymLabel /MG+Label.swift +++ b/Projects/Modules/DSKit/Sources/Components/MaeumGaGymLabel /MG+Label.swift @@ -51,6 +51,10 @@ open class MGLabel: BaseLabel { public func changeText(text: String?) { textLabel.text = text } + + public func changeFont(font: UIFont?) { + textLabel.font = font + } public override func layout() { super.layout() From 7eb5707b688de4bc1ca0e6b55eb9b09296cf9d0c Mon Sep 17 00:00:00 2001 From: s Date: Sat, 23 Mar 2024 20:46:54 +0900 Subject: [PATCH 17/45] =?UTF-8?q?FIX=20::=20[IOS-47]=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=20=EC=A3=BC=EC=84=9D=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MaeumGaGymVIew/MG+AgreeView.swift | 290 +++++++++--------- 1 file changed, 145 insertions(+), 145 deletions(-) diff --git a/Projects/Modules/DSKit/Sources/Components/MaeumGaGymVIew/MG+AgreeView.swift b/Projects/Modules/DSKit/Sources/Components/MaeumGaGymVIew/MG+AgreeView.swift index cfbe6e08..dbd60ace 100644 --- a/Projects/Modules/DSKit/Sources/Components/MaeumGaGymVIew/MG+AgreeView.swift +++ b/Projects/Modules/DSKit/Sources/Components/MaeumGaGymVIew/MG+AgreeView.swift @@ -1,145 +1,145 @@ -import UIKit -import SnapKit -import Then -import RxSwift -import RxCocoa - -open class MGAgreeView: UIView { - - let disposeBag = DisposeBag() - - private let decorateLine1 = MGLine() - public let allAgreeButton = MGAgreeButton(text: .allAgreeText, font: UIFont.Pretendard.labelLarge) - private let decorateLine2 = MGLine() - - public let firstAgreeButton = MGAgreeButton(text: .privacyAgreeText, readMoreType: true) - public let secondAgreeButton = MGAgreeButton(text: .termsAgreeText, readMoreType: true) - public let thirdAgreeButton = MGAgreeButton(text: .ageAgreeText) - public let fourthAgreeButton = MGAgreeButton(text: .marketingAgreeText, chooseType: true) - - private var blueColor = DSKitAsset.Colors.blue500.color - private var grayColor = DSKitAsset.Colors.gray400.color - - public init () { - super.init(frame: .zero) - setupUI() - } - - public init(firstAgreeText: agreeButtonTextType, - firstReadMoreType: Bool? = false, - secondAgreeText: agreeButtonTextType, - secondReadMoreType: Bool? = false, - thirdAgreeText: agreeButtonTextType, - thirdReadMoreType: Bool? = false, - fourthAgreeText: agreeButtonTextType, - fourthReadMoreType: Bool? = false - ) { - super.init(frame: .zero) - - firstAgreeButton.editButtonType(text: firstAgreeText.message, readMoreType: firstReadMoreType) - secondAgreeButton.editButtonType(text: secondAgreeText.message, readMoreType: secondReadMoreType) - thirdAgreeButton.editButtonType(text: thirdAgreeText.message, readMoreType: thirdReadMoreType) - fourthAgreeButton.editButtonType(text: fourthAgreeText.message, readMoreType: fourthReadMoreType) - - setupUI() - } - - required public init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupUI() { - self.snp.makeConstraints { - $0.width.equalTo(390.0) - $0.height.equalTo(284.0) - } - - addSubviews([decorateLine1, - allAgreeButton, - decorateLine2, - firstAgreeButton, - secondAgreeButton, - thirdAgreeButton, - fourthAgreeButton]) - - decorateLine1.snp.makeConstraints { - $0.top.equalToSuperview() - $0.centerX.equalToSuperview() - } - - allAgreeButton.snp.makeConstraints { - $0.top.equalTo(decorateLine1.snp.bottom).offset(12.0) - $0.centerX.equalToSuperview() - } - - decorateLine2.snp.makeConstraints { - $0.top.equalTo(allAgreeButton.snp.bottom).offset(12.0) - $0.centerX.equalToSuperview() - } - - firstAgreeButton.snp.makeConstraints { - $0.top.equalTo(decorateLine2.snp.bottom).offset(8.0) - $0.centerX.equalToSuperview() - } - - secondAgreeButton.snp.makeConstraints { - $0.top.equalTo(firstAgreeButton.snp.bottom).offset(8.0) - $0.centerX.equalToSuperview() - } - - thirdAgreeButton.snp.makeConstraints { - $0.top.equalTo(secondAgreeButton.snp.bottom).offset(8.0) - $0.centerX.equalToSuperview() - } - - fourthAgreeButton.snp.makeConstraints { - $0.top.equalTo(thirdAgreeButton.snp.bottom).offset(8.0) - $0.centerX.equalToSuperview() - } - } - - public var allAgreeButtonState: Bool { - return firstAgreeButton.checked && - secondAgreeButton.checked && - thirdAgreeButton.checked && - fourthAgreeButton.checked - } - - public func setAllAgreeButtonState(_ isEnabled: Bool) { - allAgreeButton.checked = isEnabled - if isEnabled { - firstAgreeButton.buttonYesChecked() - secondAgreeButton.buttonYesChecked() - thirdAgreeButton.buttonYesChecked() - fourthAgreeButton.buttonYesChecked() - updateAllAgreeButtonState() - } else { - firstAgreeButton.buttonNoChecked() - secondAgreeButton.buttonNoChecked() - thirdAgreeButton.buttonNoChecked() - fourthAgreeButton.buttonNoChecked() - updateAllAgreeButtonState() - } - } - - public func updateAllAgreeButtonState() { - if allAgreeButtonState == true { - allAgreeButton.buttonYesChecked() - } else { - allAgreeButton.buttonNoChecked() - } - } - - public func buttonActivationChecked(button: MGCheckButton) -> Bool { - let shouldActivateButton = firstAgreeButton.checked && - secondAgreeButton.checked && - thirdAgreeButton.checked && - !fourthAgreeButton.checked - - button.isEnabled = shouldActivateButton - button.backgroundColor = shouldActivateButton ? blueColor : grayColor - button.textLabel.textColor = shouldActivateButton ? .white : DSKitAsset.Colors.gray200.color - - return shouldActivateButton - } -} +//import UIKit +//import SnapKit +//import Then +//import RxSwift +//import RxCocoa +// +//open class MGAgreeView: UIView { +// +// let disposeBag = DisposeBag() +// +// private let decorateLine1 = MGLine() +// public let allAgreeButton = MGAgreeButton(text: .allAgreeText, font: UIFont.Pretendard.labelLarge) +// private let decorateLine2 = MGLine() +// +// public let firstAgreeButton = MGAgreeButton(text: .privacyAgreeText, readMoreType: true) +// public let secondAgreeButton = MGAgreeButton(text: .termsAgreeText, readMoreType: true) +// public let thirdAgreeButton = MGAgreeButton(text: .ageAgreeText) +// public let fourthAgreeButton = MGAgreeButton(text: .marketingAgreeText, chooseType: true) +// +// private var blueColor = DSKitAsset.Colors.blue500.color +// private var grayColor = DSKitAsset.Colors.gray400.color +// +// public init () { +// super.init(frame: .zero) +// setupUI() +// } +// +// public init(firstAgreeText: agreeButtonTextType, +// firstReadMoreType: Bool? = false, +// secondAgreeText: agreeButtonTextType, +// secondReadMoreType: Bool? = false, +// thirdAgreeText: agreeButtonTextType, +// thirdReadMoreType: Bool? = false, +// fourthAgreeText: agreeButtonTextType, +// fourthReadMoreType: Bool? = false +// ) { +// super.init(frame: .zero) +// +// firstAgreeButton.editButtonType(text: firstAgreeText.message, readMoreType: firstReadMoreType) +// secondAgreeButton.editButtonType(text: secondAgreeText.message, readMoreType: secondReadMoreType) +// thirdAgreeButton.editButtonType(text: thirdAgreeText.message, readMoreType: thirdReadMoreType) +// fourthAgreeButton.editButtonType(text: fourthAgreeText.message, readMoreType: fourthReadMoreType) +// +// setupUI() +// } +// +// required public init?(coder: NSCoder) { +// fatalError("init(coder:) has not been implemented") +// } +// +// private func setupUI() { +// self.snp.makeConstraints { +// $0.width.equalTo(390.0) +// $0.height.equalTo(284.0) +// } +// +// addSubviews([decorateLine1, +// allAgreeButton, +// decorateLine2, +// firstAgreeButton, +// secondAgreeButton, +// thirdAgreeButton, +// fourthAgreeButton]) +// +// decorateLine1.snp.makeConstraints { +// $0.top.equalToSuperview() +// $0.centerX.equalToSuperview() +// } +// +// allAgreeButton.snp.makeConstraints { +// $0.top.equalTo(decorateLine1.snp.bottom).offset(12.0) +// $0.centerX.equalToSuperview() +// } +// +// decorateLine2.snp.makeConstraints { +// $0.top.equalTo(allAgreeButton.snp.bottom).offset(12.0) +// $0.centerX.equalToSuperview() +// } +// +// firstAgreeButton.snp.makeConstraints { +// $0.top.equalTo(decorateLine2.snp.bottom).offset(8.0) +// $0.centerX.equalToSuperview() +// } +// +// secondAgreeButton.snp.makeConstraints { +// $0.top.equalTo(firstAgreeButton.snp.bottom).offset(8.0) +// $0.centerX.equalToSuperview() +// } +// +// thirdAgreeButton.snp.makeConstraints { +// $0.top.equalTo(secondAgreeButton.snp.bottom).offset(8.0) +// $0.centerX.equalToSuperview() +// } +// +// fourthAgreeButton.snp.makeConstraints { +// $0.top.equalTo(thirdAgreeButton.snp.bottom).offset(8.0) +// $0.centerX.equalToSuperview() +// } +// } +// +// public var allAgreeButtonState: Bool { +// return firstAgreeButton.checked && +// secondAgreeButton.checked && +// thirdAgreeButton.checked && +// fourthAgreeButton.checked +// } +// +// public func setAllAgreeButtonState(_ isEnabled: Bool) { +// allAgreeButton.checked = isEnabled +// if isEnabled { +// firstAgreeButton.buttonYesChecked() +// secondAgreeButton.buttonYesChecked() +// thirdAgreeButton.buttonYesChecked() +// fourthAgreeButton.buttonYesChecked() +// updateAllAgreeButtonState() +// } else { +// firstAgreeButton.buttonNoChecked() +// secondAgreeButton.buttonNoChecked() +// thirdAgreeButton.buttonNoChecked() +// fourthAgreeButton.buttonNoChecked() +// updateAllAgreeButtonState() +// } +// } +// +// public func updateAllAgreeButtonState() { +// if allAgreeButtonState == true { +// allAgreeButton.buttonYesChecked() +// } else { +// allAgreeButton.buttonNoChecked() +// } +// } +// +// public func buttonActivationChecked(button: MGCheckButton) -> Bool { +// let shouldActivateButton = firstAgreeButton.checked && +// secondAgreeButton.checked && +// thirdAgreeButton.checked && +// !fourthAgreeButton.checked +// +// button.isEnabled = shouldActivateButton +// button.backgroundColor = shouldActivateButton ? blueColor : grayColor +// button.textLabel.textColor = shouldActivateButton ? .white : DSKitAsset.Colors.gray200.color +// +// return shouldActivateButton +// } +//} From 802451201d851312f03e134869ee4bc67e8fe0f3 Mon Sep 17 00:00:00 2001 From: s Date: Sat, 23 Mar 2024 20:47:19 +0900 Subject: [PATCH 18/45] =?UTF-8?q?REFACT=20::=20[IOS-47]=20MG+AgreeButton?= =?UTF-8?q?=20=EB=A6=AC=ED=8E=99=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Agree/AgreeButtonType.swift | 16 +++ .../Agree/MG+AgreeButton.swift | 119 ++++-------------- 2 files changed, 42 insertions(+), 93 deletions(-) diff --git a/Projects/Modules/DSKit/Sources/Components/MaeumGaGymButton/Agree/AgreeButtonType.swift b/Projects/Modules/DSKit/Sources/Components/MaeumGaGymButton/Agree/AgreeButtonType.swift index 355e58cd..3eb0ed39 100644 --- a/Projects/Modules/DSKit/Sources/Components/MaeumGaGymButton/Agree/AgreeButtonType.swift +++ b/Projects/Modules/DSKit/Sources/Components/MaeumGaGymButton/Agree/AgreeButtonType.swift @@ -1,3 +1,4 @@ +import UIKit import Foundation public enum agreeButtonTextType { @@ -21,4 +22,19 @@ public enum agreeButtonTextType { return "모두 동의해요" } } + + var font: UIFont { + switch self { + case .privacyAgreeText: + return UIFont.Pretendard.bodyMedium + case .termsAgreeText: + return UIFont.Pretendard.bodyMedium + case .ageAgreeText: + return UIFont.Pretendard.bodyMedium + case .marketingAgreeText: + return UIFont.Pretendard.bodyMedium + case .allAgreeText: + return UIFont.Pretendard.labelLarge + } + } } diff --git a/Projects/Modules/DSKit/Sources/Components/MaeumGaGymButton/Agree/MG+AgreeButton.swift b/Projects/Modules/DSKit/Sources/Components/MaeumGaGymButton/Agree/MG+AgreeButton.swift index f4cc6fdc..fb2a21c1 100644 --- a/Projects/Modules/DSKit/Sources/Components/MaeumGaGymButton/Agree/MG+AgreeButton.swift +++ b/Projects/Modules/DSKit/Sources/Components/MaeumGaGymButton/Agree/MG+AgreeButton.swift @@ -9,15 +9,15 @@ import SnapKit import Core open class MGAgreeButton: BaseButton { - - public var checked: Bool = false - public var iconImageView = UIImageView().then { + public var checked: Bool = false + + private var iconImageView = UIImageView().then { $0.image = DSKitAsset.Assets.noCheckActIcon.image } private var textLabel = MGLabel(numberOfLineCount: 1) - + private let chooseLabel = MGLabel(text: "(선택)", font: UIFont.Pretendard.bodyMedium, textColor: DSKitAsset.Colors.gray400.color, @@ -26,136 +26,69 @@ open class MGAgreeButton: BaseButton { ).then { $0.isHidden = true } - - private let readMore = MGButton( - titleText: "자세히 보기", - font: UIFont.Pretendard.labelSmall, - textColor: DSKitAsset.Colors.gray300.color - ).then { - $0.isHidden = false - } - - private let readMoreLine = MGLine(lineColor: DSKitAsset.Colors.gray300.color, - lineWidth: 64.0, - lineHeight: 1.0 - ) public init ( - text: agreeButtonTextType, - font: UIFont? = UIFont.Pretendard.bodyMedium, - type: Int? = 1, - readMoreType: Bool? = false, + type: agreeButtonTextType, chooseType: Bool? = false ) { super.init(frame: .zero) - - setupUI(textType: text, - font: font, - type: type, - readMoreType: readMoreType, - chooseType: chooseType) + + setup(textType: type, chooseType: chooseType) } - + required public init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + public override func layout() { super.layout() - - self.addSubviews([iconImageView, textLabel, chooseLabel, readMoreLine, readMore]) - - self.snp.makeConstraints { - $0.width.equalTo(390.0) - $0.height.equalTo(44.0) - } - + + self.addSubviews([iconImageView, textLabel, chooseLabel]) + iconImageView.snp.makeConstraints { $0.width.height.equalTo(28.0) - $0.leading.equalToSuperview().offset(0.0) + $0.leading.equalToSuperview() $0.centerY.equalToSuperview() } - + textLabel.snp.makeConstraints { $0.leading.equalTo(iconImageView.snp.trailing).offset(12.0) $0.centerY.equalToSuperview() $0.height.equalTo(20.0) } - + chooseLabel.snp.makeConstraints { $0.height.equalTo(20.0) $0.centerY.equalToSuperview() $0.leading.equalTo(textLabel.snp.trailing).offset(5.0) } - - readMoreLine.snp.makeConstraints { - $0.trailing.equalToSuperview().offset(0.0) - $0.bottom.equalToSuperview().offset(-13.0) - } - - readMore.snp.makeConstraints { - $0.width.equalTo(64.0) - $0.height.equalToSuperview() - $0.trailing.equalToSuperview().offset(0.0) - $0.top.equalToSuperview().offset(0.0) - } - } - - open override func buttonAction() { - super.buttonAction() - buttonTapped() } - + public func buttonYesChecked() { checked = true iconImageView.image = DSKitAsset.Assets.yesCheckActIcon.image } - + public func buttonNoChecked() { checked = false iconImageView.image = DSKitAsset.Assets.noCheckActIcon.image } - public func editButtonType(text: String, readMoreType: Bool? = false) { - self.textLabel.text = text - - readMore.isHidden = !(readMoreType ?? true) - readMoreLine.isHidden = !(readMoreType ?? true) + public override func buttonAction() { + self.rx.tap.subscribe(onNext: { [self] in + checked ? buttonNoChecked() : buttonYesChecked() + }).disposed(by: disposeBag) } } private extension MGAgreeButton { - func setupUI(textType: agreeButtonTextType, - font: UIFont?, - type: Int?, - readMoreType: Bool?, - chooseType: Bool? + func setup(textType: agreeButtonTextType, + chooseType: Bool? = false ) { - self.textLabel.text = textType.message - self.textLabel.font = font - - setOptionalViewVisibility(shouldShow: chooseType ?? true) - - if (type != nil) != false { + textLabel.changeText(text: textType.message) + textLabel.changeFont(font: textType.font) + if chooseType == true { chooseLabel.isHidden = false } } - - func buttonTapped() { - rx.tap - .subscribe(onNext: { [self] in - checked ? buttonNoChecked() : buttonYesChecked() - }).disposed(by: disposeBag) - - readMore.rx.tap - .subscribe(onNext: { - print("자세히 보기 클릭 됨") - }).disposed(by: disposeBag) - } - - func setOptionalViewVisibility(shouldShow: Bool) { - chooseLabel.isHidden = !shouldShow - readMore.isHidden = !shouldShow - readMoreLine.isHidden = !shouldShow - } } From 60d60d3f0b656f1be391376a3d363ba25ecad338 Mon Sep 17 00:00:00 2001 From: s Date: Sat, 23 Mar 2024 20:48:02 +0900 Subject: [PATCH 19/45] =?UTF-8?q?BURN=20::=20[IOS-47]=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/Domain/Sources/Response/dummy4.swift | 1 - 1 file changed, 1 deletion(-) delete mode 100644 Projects/Domain/Sources/Response/dummy4.swift diff --git a/Projects/Domain/Sources/Response/dummy4.swift b/Projects/Domain/Sources/Response/dummy4.swift deleted file mode 100644 index fecc4ab4..00000000 --- a/Projects/Domain/Sources/Response/dummy4.swift +++ /dev/null @@ -1 +0,0 @@ -import Foundation From 40b724c77b6221ef8c8520e6880b5ad792477169 Mon Sep 17 00:00:00 2001 From: s Date: Sun, 24 Mar 2024 23:21:42 +0900 Subject: [PATCH 20/45] =?UTF-8?q?FIX=20::=20[IOS-47]=20=EB=A6=AC=ED=8E=99?= =?UTF-8?q?=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Modules/MGNetworks/Sources/API/Auth/AppleAPI.swift | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Projects/Modules/MGNetworks/Sources/API/Auth/AppleAPI.swift b/Projects/Modules/MGNetworks/Sources/API/Auth/AppleAPI.swift index 38df6ded..757470eb 100644 --- a/Projects/Modules/MGNetworks/Sources/API/Auth/AppleAPI.swift +++ b/Projects/Modules/MGNetworks/Sources/API/Auth/AppleAPI.swift @@ -11,7 +11,7 @@ public enum AppleAPI { } extension AppleAPI: BaseAPI { - + public static var apiType: APIType = .apple public var path: String { @@ -39,11 +39,13 @@ extension AppleAPI: BaseAPI { public var task: Moya.Task { switch self { case let .appleSignup(nickname, accessToken): - let parameters: [String: Any] = [ - "nickname": nickname, + let bodyParameters: [String: Any] = [ + "nickname": nickname + ] + let urlParameters: [String: Any] = [ "access_token": accessToken ] - return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) + return .requestCompositeParameters(bodyParameters: bodyParameters, bodyEncoding: JSONEncoding.default, urlParameters: urlParameters) case let .appleLogin(accessToken): let parameters: [String: Any] = [ "access_token": accessToken From ed1e71d73dc89c17c5b13e17a43438b6fc64efe9 Mon Sep 17 00:00:00 2001 From: s Date: Mon, 25 Mar 2024 22:23:58 +0900 Subject: [PATCH 21/45] =?UTF-8?q?ADD=20::=20[IOS-47]=20response=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RepositoryInterface/AuthRepositoryInterface.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Projects/Domain/Sources/RepositoryInterface/AuthRepositoryInterface.swift b/Projects/Domain/Sources/RepositoryInterface/AuthRepositoryInterface.swift index ef38e688..ef788647 100644 --- a/Projects/Domain/Sources/RepositoryInterface/AuthRepositoryInterface.swift +++ b/Projects/Domain/Sources/RepositoryInterface/AuthRepositoryInterface.swift @@ -2,6 +2,7 @@ import UIKit import RxSwift import RxCocoa +import Moya public enum OauthType { case google @@ -14,8 +15,8 @@ public protocol AuthRepositoryInterface { func kakaoToken() -> Single func appleToken() -> Single func appleButtonTap() -> Single - func oauthSignup(nickname: String, accessToken: String, oauth: OauthType) -> Observable - func oauthLogin(accessToken: String, oauth: OauthType) -> Observable - func oauthRecovery(accessToken: String, oauth: OauthType) -> Observable + func oauthSignup(nickname: String, accessToken: String, oauth: OauthType) -> Observable + func oauthLogin(accessToken: String, oauth: OauthType) -> Single + func oauthRecovery(accessToken: String, oauth: OauthType) -> Observable func getIntroData() -> Single } From 7905ec3d065d31a7cac2700f08ee5f8c24fe68e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=80=ED=98=B8?= <127753071+Eunho0922@users.noreply.github.com> Date: Tue, 26 Mar 2024 19:05:42 +0900 Subject: [PATCH 22/45] =?UTF-8?q?ADD=20::=20[IOS-47]=20=EC=9D=B8=ED=8A=B8?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Icons/IntroIcon.imageset/Contents.json | 23 ++++++++++++++++++ .../Icons/IntroIcon.imageset/IntroIcon.png | Bin 0 -> 7970 bytes .../Icons/IntroIcon.imageset/IntroIcon@2x.png | Bin 0 -> 17621 bytes .../Icons/IntroIcon.imageset/IntroIcon@3x.png | Bin 0 -> 31260 bytes 4 files changed, 23 insertions(+) create mode 100644 Projects/Modules/DSKit/Resources/Assets.xcassets/Icons/IntroIcon.imageset/Contents.json create mode 100644 Projects/Modules/DSKit/Resources/Assets.xcassets/Icons/IntroIcon.imageset/IntroIcon.png create mode 100644 Projects/Modules/DSKit/Resources/Assets.xcassets/Icons/IntroIcon.imageset/IntroIcon@2x.png create mode 100644 Projects/Modules/DSKit/Resources/Assets.xcassets/Icons/IntroIcon.imageset/IntroIcon@3x.png diff --git a/Projects/Modules/DSKit/Resources/Assets.xcassets/Icons/IntroIcon.imageset/Contents.json b/Projects/Modules/DSKit/Resources/Assets.xcassets/Icons/IntroIcon.imageset/Contents.json new file mode 100644 index 00000000..f89881a7 --- /dev/null +++ b/Projects/Modules/DSKit/Resources/Assets.xcassets/Icons/IntroIcon.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "IntroIcon.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "IntroIcon@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "IntroIcon@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/Modules/DSKit/Resources/Assets.xcassets/Icons/IntroIcon.imageset/IntroIcon.png b/Projects/Modules/DSKit/Resources/Assets.xcassets/Icons/IntroIcon.imageset/IntroIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..72002370f55d16f745d3fef5e779c96ab858a4fd GIT binary patch literal 7970 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 literal 0 HcmV?d00001 diff --git a/Projects/Modules/DSKit/Resources/Assets.xcassets/Icons/IntroIcon.imageset/IntroIcon@2x.png b/Projects/Modules/DSKit/Resources/Assets.xcassets/Icons/IntroIcon.imageset/IntroIcon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..da07fe771ad67f5a77c02aa273491e7bf4cf5098 GIT binary patch literal 17621 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 literal 0 HcmV?d00001 diff --git a/Projects/Modules/DSKit/Resources/Assets.xcassets/Icons/IntroIcon.imageset/IntroIcon@3x.png b/Projects/Modules/DSKit/Resources/Assets.xcassets/Icons/IntroIcon.imageset/IntroIcon@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..8644478e33033aa01fb4e9c732c45c097390109e GIT binary patch literal 31260 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 U|9Fzf|IDm;TUWJI*)s6|0JL=gtpET3 literal 0 HcmV?d00001 From e7c00dc577ff4bca34cf3859eb476df4ae9f1c17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=80=ED=98=B8?= <127753071+Eunho0922@users.noreply.github.com> Date: Tue, 26 Mar 2024 20:23:24 +0900 Subject: [PATCH 23/45] =?UTF-8?q?FIX=20::=20[IOS-47]=20authUseCase=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Domain/Sources/UseCase/AuthUseCase.swift | 123 ++++++++++++++---- 1 file changed, 97 insertions(+), 26 deletions(-) diff --git a/Projects/Domain/Sources/UseCase/AuthUseCase.swift b/Projects/Domain/Sources/UseCase/AuthUseCase.swift index 8f631b8e..607a67bb 100644 --- a/Projects/Domain/Sources/UseCase/AuthUseCase.swift +++ b/Projects/Domain/Sources/UseCase/AuthUseCase.swift @@ -14,6 +14,8 @@ public protocol AuthUseCase { func kakaoButtonTap() func getIntroData() func appleButtonTap() + func nextButtonTap(nickname: String) + func setOauthType(oauthType: OauthType) // func appleNickNameButtonTap() -> Single var appleSignupResult: PublishSubject { get } var introData: PublishSubject { get } @@ -23,46 +25,115 @@ public class DefaultAuthUseCase { private let authRepository: AuthRepositoryInterface private let disposeBag = DisposeBag() + open var oauthType: OauthType! + public let introData = PublishSubject() public let appleSignupResult = PublishSubject() public let appleNicknameSignupResult = PublishSubject() - public init(authRepository: AuthRepositoryInterface) { self.authRepository = authRepository } } extension DefaultAuthUseCase: AuthUseCase { + public func setOauthType(oauthType: OauthType) { + self.oauthType = oauthType + } + public func nextButtonTap(nickname: String) { + let accessToken = TokenManagerImpl().get(key: KeychainType.authorizationToken)! + authRepository.oauthSignup(nickname: nickname, + accessToken: accessToken, + oauth: self.oauthType) + .subscribe(onNext: { [self] element in + MGLogger.debug("nicknameButtonTap : \(element)") + authRepository.oauthLogin(accessToken: accessToken, oauth: self.oauthType) + .subscribe(onSuccess: { [self] element in + MGLogger.debug("nicknameButtonTap Login : \(element)") + AuthStepper.shared.steps.accept(MGStep.authCompleteIsRequired) + }, onFailure: { error in + MGLogger.debug("nicknameButtonTap Login Error : \(error)") + }).disposed(by: disposeBag) + }, onError: { error in + MGLogger.debug("nicnameButtonTap Error : \(error)") + }).disposed(by: disposeBag) + } + public func appleButtonTap() { authRepository.appleButtonTap() - .subscribe( - onSuccess: { [self] token in - appleSignupResult.onNext(token) - TokenManagerImpl().save(token: token, with: KeychainType.authorizationToken) - authRepository.oauthLogin(accessToken: token, oauth: .apple) - .subscribe(onNext: { element in - MGLogger.debug("\(element)") - AuthStepper.shared.steps.accept(MGStep.authCompleteIsRequired) - }, onError: { error in - MGLogger.debug("appleLogin : \(error)") - self.authRepository.oauthRecovery(accessToken: token, oauth: .apple) - .subscribe(onNext: { element in - MGLogger.debug("\(element)") - }, onError: { error in - MGLogger.debug("\(error)") - AuthStepper.shared.steps.accept(MGStep.authAgreeIsRequired) - }).disposed(by: self.disposeBag) - } - ).disposed(by: disposeBag) - }, - onFailure: { [weak self] error in - self?.appleSignupResult.onError(error) - } - ) - .disposed(by: disposeBag) + .subscribe(onSuccess: { [self] token in + MGLogger.debug("appleButtonTap token ✅ \(token)") + TokenManagerImpl().save(token: token, with: KeychainType.authorizationToken) + authRepository.oauthLogin(accessToken: token, oauth: .apple) + .subscribe(onSuccess: { element in + MGLogger.debug("appleButtonTap login ✅ \(String(describing: element.response))") + AuthStepper.shared.steps.accept(MGStep.authCompleteIsRequired) + }, onFailure: { [self] error in + MGLogger.debug("appleButtonTap login ❌ \(error)") + authRepository.oauthRecovery(accessToken: token, oauth: .apple) + .subscribe(onSuccess: { [self] element in + MGLogger.debug("appleButtonTap recovery ✅ \(element)") + authRepository.oauthLogin(accessToken: token, oauth: .apple) + .subscribe(onSuccess: { element in + MGLogger.debug("appleButtonTap login ✅ \(String(describing: element.response))") + }, onFailure: { error in + MGLogger.debug("appleButtonTap login(여기서 절대 에러가 나면 안됩니다...) ❌ \(error)") + }).disposed(by: disposeBag) + }, onFailure: { error in + MGLogger.debug("appleButtonTap recovery ❌ \(error)") + AuthStepper.shared.steps.accept(MGStep.authAgreeIsRequired) + }) + }).disposed(by: disposeBag) + }, onFailure: { error in + MGLogger.debug("appleButtonTap token error ❌ \(error)") + }).disposed(by: disposeBag) } + +// public func appleButtonTap() { +// authRepository.appleButtonTap() +// .subscribe(onSuccess: { [self] token in +// TokenManagerImpl().save(token: token, with: KeychainType.authorizationToken) +// authRepository.oauthLogin(accessToken: token, oauth: .apple) +// .subscribe(onSuccess: { element in +//// TokenManagerImpl().save(token: (element.response?.allHeaderFields["Authorization"] as? String)!, with: .authorizationToken) +//// TokenManagerImpl().save(token: (element.response?.allHeaderFields["RF-TOKEN"] as? String)!, with: .refreshToken) +// MGLogger.debug("loginㄴㅇㄹㄴㅇㄹ : \(element.response?.description)") +//// MGLogger.debug("login : \(TokenManagerImpl().get(key: .authorizationToken))") +//// MGLogger.debug("login : \(TokenManagerImpl().get(key: .refreshToken))") +// +// AuthStepper.shared.steps.accept(MGStep.authCompleteIsRequired) +// }, onFailure: { [self] error in +// MGLogger.debug("appleLogin : \(error)") +// authRepository.oauthRecovery(accessToken: token, oauth: .apple) +// .subscribe(onNext: { element in +// MGLogger.debug("success : \(element)") +// }, onError: { [self] recoveryError in +// MGLogger.debug("error !!!! \(recoveryError)") +// authRepository.oauthSignup(nickname: "이은호", accessToken: token, oauth: .apple) +// .subscribe(onNext: { element in +// MGLogger.debug("signup : \(element)") +// self.authRepository.oauthLogin(accessToken: token, oauth: .apple).subscribe(onSuccess: { response in +// MGLogger.debug("login : \(String(describing: response.response?.headers))") +// }, onFailure: { error in +// MGLogger.debug("login error : \(error)") +// } +// ).disposed(by: self.disposeBag) +// }, onError: { error in +// MGLogger.debug("signup error : \(error)") +// } +// ).disposed(by: disposeBag) +//// AuthStepper.shared.steps.accept(MGStep.authAgreeIsRequired) +// }).disposed(by: disposeBag) +// } +// ).disposed(by: disposeBag) +// }, +// onFailure: { [weak self] error in +// self?.appleSignupResult.onError(error) +// } +// ) +// .disposed(by: disposeBag) +// } public func getIntroData() { return authRepository.getIntroData() From 0021f7714f65ccbfc97d0e572f294300a9e07c61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=80=ED=98=B8?= <127753071+Eunho0922@users.noreply.github.com> Date: Wed, 27 Mar 2024 08:49:05 +0900 Subject: [PATCH 24/45] =?UTF-8?q?ADD=20::=20[IOS-47]=20AuthAPI=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 삭제, 닉네임 중복 확인, 토큰 재발급 --- .../MGNetworks/Sources/API/Auth/AuthAPI.swift | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 Projects/Modules/MGNetworks/Sources/API/Auth/AuthAPI.swift diff --git a/Projects/Modules/MGNetworks/Sources/API/Auth/AuthAPI.swift b/Projects/Modules/MGNetworks/Sources/API/Auth/AuthAPI.swift new file mode 100644 index 00000000..4a5b38f4 --- /dev/null +++ b/Projects/Modules/MGNetworks/Sources/API/Auth/AuthAPI.swift @@ -0,0 +1,59 @@ +import Foundation + +import Moya +import Core +import Alamofire + +public enum AuthAPI { + case delete(accessToken: String) + case reissuanceToken(refreshToken: String) + case nickname(nickname: String) +} + +extension AuthAPI: BaseAPI { + + public static var apiType: APIType = .auth + + public var path: String { + switch self { + case .delete: + return "" + case .reissuanceToken: + return "/re-issue" + case let .nickname(nickname): + return "/nickname/\(nickname)" + } + } + + public var method: Moya.Method { + switch self { + case .delete: + return .delete + case .reissuanceToken: + return .get + case .nickname: + return .get + } + } + + public var task: Moya.Task { + switch self { + case .delete, .nickname: + return .requestPlain + case let .reissuanceToken(refreshToken): + let parameters: [String: Any] = [ + "refresh_token": refreshToken + ] + return .requestParameters(parameters: parameters, encoding: URLEncoding.default) + } + } + + public var headers: [String : String]? { + switch self { + case let .delete(accessToken): + return ["Authorization": "\(accessToken)"] + default: + return nil + } + } +} From 6cfb0ba6026b4ce2160977b4eb08a3fe1214c875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=80=ED=98=B8?= <127753071+Eunho0922@users.noreply.github.com> Date: Wed, 27 Mar 2024 08:49:25 +0900 Subject: [PATCH 25/45] ADD :: [IOS-47] BaseAPI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit auth 추가 --- .../Modules/MGNetworks/Sources/Foundation/Base/BaseAPI.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Projects/Modules/MGNetworks/Sources/Foundation/Base/BaseAPI.swift b/Projects/Modules/MGNetworks/Sources/Foundation/Base/BaseAPI.swift index 7acfdc0d..5c03e9dd 100644 --- a/Projects/Modules/MGNetworks/Sources/Foundation/Base/BaseAPI.swift +++ b/Projects/Modules/MGNetworks/Sources/Foundation/Base/BaseAPI.swift @@ -13,7 +13,7 @@ public protocol BaseAPI: TargetType { extension BaseAPI { public var baseURL: URL { - var appURL = Config.setConfigTypeAndGetBaseURL(.application) + var appURL = Config.setConfigTypeAndGetBaseURL(.devTest) // var devURL = Config.setConfigTypeAndGetBaseURL(.devTest) switch Self.apiType { @@ -25,6 +25,8 @@ extension BaseAPI { appURL += "/google" case .apple: appURL += "/apple" + case .auth: + appURL += "/auth" } guard let url = URL(string: appURL) else { From a5aadac40287e109809b7720b751acad45199232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=80=ED=98=B8?= <127753071+Eunho0922@users.noreply.github.com> Date: Wed, 27 Mar 2024 08:49:40 +0900 Subject: [PATCH 26/45] =?UTF-8?q?ADD=20::=20[IOS-47]=20APIType=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 --- .../Modules/MGNetworks/Sources/Foundation/Base/APIType.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Projects/Modules/MGNetworks/Sources/Foundation/Base/APIType.swift b/Projects/Modules/MGNetworks/Sources/Foundation/Base/APIType.swift index 77bea87d..9dde3637 100644 --- a/Projects/Modules/MGNetworks/Sources/Foundation/Base/APIType.swift +++ b/Projects/Modules/MGNetworks/Sources/Foundation/Base/APIType.swift @@ -3,7 +3,8 @@ import RxMoya import Core public enum APIType { - + + case auth case CSRFToken // Oauth From 566da3b85ae1221f6413aa1d63f7973bf8074692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=80=ED=98=B8?= <127753071+Eunho0922@users.noreply.github.com> Date: Wed, 27 Mar 2024 09:05:07 +0900 Subject: [PATCH 27/45] ADD :: [IOS-47] AuthUseCase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit appleBuuttonTap 수정, nextButtonTapp 추가(닉네임 입력) --- .../Domain/Sources/UseCase/AuthUseCase.swift | 96 ++++++------------- 1 file changed, 27 insertions(+), 69 deletions(-) diff --git a/Projects/Domain/Sources/UseCase/AuthUseCase.swift b/Projects/Domain/Sources/UseCase/AuthUseCase.swift index 607a67bb..177adbf1 100644 --- a/Projects/Domain/Sources/UseCase/AuthUseCase.swift +++ b/Projects/Domain/Sources/UseCase/AuthUseCase.swift @@ -14,9 +14,7 @@ public protocol AuthUseCase { func kakaoButtonTap() func getIntroData() func appleButtonTap() - func nextButtonTap(nickname: String) - func setOauthType(oauthType: OauthType) -// func appleNickNameButtonTap() -> Single + func changeNickname(nickname: String) var appleSignupResult: PublishSubject { get } var introData: PublishSubject { get } } @@ -26,6 +24,7 @@ public class DefaultAuthUseCase { private let disposeBag = DisposeBag() open var oauthType: OauthType! + public var nicknameText: String = "" public let introData = PublishSubject() public let appleSignupResult = PublishSubject() @@ -37,30 +36,38 @@ public class DefaultAuthUseCase { } extension DefaultAuthUseCase: AuthUseCase { - public func setOauthType(oauthType: OauthType) { - self.oauthType = oauthType + + public func changeNickname(nickname: String) { + self.nicknameText = nickname } - public func nextButtonTap(nickname: String) { + public func nextButtonTap() { let accessToken = TokenManagerImpl().get(key: KeychainType.authorizationToken)! - authRepository.oauthSignup(nickname: nickname, - accessToken: accessToken, - oauth: self.oauthType) - .subscribe(onNext: { [self] element in - MGLogger.debug("nicknameButtonTap : \(element)") - authRepository.oauthLogin(accessToken: accessToken, oauth: self.oauthType) + authRepository.nicknameCheck(nickname: nicknameText) + .subscribe(onSuccess: { [self] element in + MGLogger.debug("nicknameButtonTap nickname ✅ \(element)") + authRepository.oauthSignup(nickname: nicknameText, + accessToken: accessToken, + oauth: self.oauthType) .subscribe(onSuccess: { [self] element in - MGLogger.debug("nicknameButtonTap Login : \(element)") - AuthStepper.shared.steps.accept(MGStep.authCompleteIsRequired) + MGLogger.debug("nicknameButtonTap Signup ✅ \(element)") + authRepository.oauthLogin(accessToken: accessToken, oauth: self.oauthType) + .subscribe(onSuccess: { element in + MGLogger.debug("nicknameButtonTap Login ✅ \(element)") + AuthStepper.shared.steps.accept(MGStep.authCompleteIsRequired) + }, onFailure: { error in + MGLogger.debug("nicknameButtonTap Login(여기선 절대 에러가 나면 안됩니다...) ❌ \(error)") + }).disposed(by: disposeBag) }, onFailure: { error in - MGLogger.debug("nicknameButtonTap Login Error : \(error)") + MGLogger.debug("nicknameButtonTap Signup(여기선 절대 에러가 나면 안됩니다...) ❌ \(error)") }).disposed(by: disposeBag) - }, onError: { error in - MGLogger.debug("nicnameButtonTap Error : \(error)") - }).disposed(by: disposeBag) + }, onFailure: { error in + MGLogger.debug("nicknameButtonTap nickname 중복 ❌ \(error)") + }).disposed(by: disposeBag) } - + public func appleButtonTap() { + oauthType = .apple authRepository.appleButtonTap() .subscribe(onSuccess: { [self] token in MGLogger.debug("appleButtonTap token ✅ \(token)") @@ -83,58 +90,13 @@ extension DefaultAuthUseCase: AuthUseCase { }, onFailure: { error in MGLogger.debug("appleButtonTap recovery ❌ \(error)") AuthStepper.shared.steps.accept(MGStep.authAgreeIsRequired) - }) + }).disposed(by: disposeBag) }).disposed(by: disposeBag) }, onFailure: { error in MGLogger.debug("appleButtonTap token error ❌ \(error)") }).disposed(by: disposeBag) } -// public func appleButtonTap() { -// authRepository.appleButtonTap() -// .subscribe(onSuccess: { [self] token in -// TokenManagerImpl().save(token: token, with: KeychainType.authorizationToken) -// authRepository.oauthLogin(accessToken: token, oauth: .apple) -// .subscribe(onSuccess: { element in -//// TokenManagerImpl().save(token: (element.response?.allHeaderFields["Authorization"] as? String)!, with: .authorizationToken) -//// TokenManagerImpl().save(token: (element.response?.allHeaderFields["RF-TOKEN"] as? String)!, with: .refreshToken) -// MGLogger.debug("loginㄴㅇㄹㄴㅇㄹ : \(element.response?.description)") -//// MGLogger.debug("login : \(TokenManagerImpl().get(key: .authorizationToken))") -//// MGLogger.debug("login : \(TokenManagerImpl().get(key: .refreshToken))") -// -// AuthStepper.shared.steps.accept(MGStep.authCompleteIsRequired) -// }, onFailure: { [self] error in -// MGLogger.debug("appleLogin : \(error)") -// authRepository.oauthRecovery(accessToken: token, oauth: .apple) -// .subscribe(onNext: { element in -// MGLogger.debug("success : \(element)") -// }, onError: { [self] recoveryError in -// MGLogger.debug("error !!!! \(recoveryError)") -// authRepository.oauthSignup(nickname: "이은호", accessToken: token, oauth: .apple) -// .subscribe(onNext: { element in -// MGLogger.debug("signup : \(element)") -// self.authRepository.oauthLogin(accessToken: token, oauth: .apple).subscribe(onSuccess: { response in -// MGLogger.debug("login : \(String(describing: response.response?.headers))") -// }, onFailure: { error in -// MGLogger.debug("login error : \(error)") -// } -// ).disposed(by: self.disposeBag) -// }, onError: { error in -// MGLogger.debug("signup error : \(error)") -// } -// ).disposed(by: disposeBag) -//// AuthStepper.shared.steps.accept(MGStep.authAgreeIsRequired) -// }).disposed(by: disposeBag) -// } -// ).disposed(by: disposeBag) -// }, -// onFailure: { [weak self] error in -// self?.appleSignupResult.onError(error) -// } -// ) -// .disposed(by: disposeBag) -// } - public func getIntroData() { return authRepository.getIntroData() .subscribe(onSuccess: { [weak self] introModel in @@ -155,8 +117,4 @@ extension DefaultAuthUseCase: AuthUseCase { }) .disposed(by: self.disposeBag) } - -// public func appleNickNameButtonTap(nickname: String) -> Single { -// return authRepository.appleSingup(nickname: nickname, accessToken: TokenManagerImpl().get(key: .authorizationToken)).mapString -// } } From 3908204293be757ff0421759b5118db1ec105814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=80=ED=98=B8?= <127753071+Eunho0922@users.noreply.github.com> Date: Wed, 27 Mar 2024 09:05:34 +0900 Subject: [PATCH 28/45] =?UTF-8?q?FIX=20::=20[IOS-47]=20Response=20?= =?UTF-8?q?=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 파라미터 변경 --- Projects/Data/Sources/Repository/AuthRepository.swift | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Projects/Data/Sources/Repository/AuthRepository.swift b/Projects/Data/Sources/Repository/AuthRepository.swift index d30272c4..ab0ad712 100644 --- a/Projects/Data/Sources/Repository/AuthRepository.swift +++ b/Projects/Data/Sources/Repository/AuthRepository.swift @@ -25,17 +25,21 @@ public class AuthRepository: AuthRepositoryInterface { return networkService.kakaoTokenState() } - public func oauthSignup(nickname: String, accessToken: String, oauth: OauthType) -> Observable { + public func oauthSignup(nickname: String, accessToken: String, oauth: OauthType) -> Single { return networkService.oauthSingup(nickname: nickname, accessToken: accessToken, oauth: oauth) } - public func oauthLogin(accessToken: String, oauth: OauthType) -> Observable { + public func oauthLogin(accessToken: String, oauth: OauthType) -> Single { return networkService.oauthLogin(accessToken: accessToken, oauth: oauth) } - public func oauthRecovery(accessToken: String, oauth: OauthType) -> Observable { + public func oauthRecovery(accessToken: String, oauth: OauthType) -> Single { return networkService.oauthRecovery(accessToken: accessToken, oauth: oauth) } + + public func nicknameCheck(nickname: String) -> Single { + return networkService.nicknameCheck(nickname: nickname) + } public func getIntroData() -> Single { return networkService.requestIntroData() From 5631598275073996f8e86cd56d3b07e03e241dbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=80=ED=98=B8?= <127753071+Eunho0922@users.noreply.github.com> Date: Wed, 27 Mar 2024 09:06:05 +0900 Subject: [PATCH 29/45] =?UTF-8?q?FIX=20::=20[IOS-47]=20response=20?= =?UTF-8?q?=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 파라미터 변경 --- .../RepositoryInterface/AuthRepositoryInterface.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Projects/Domain/Sources/RepositoryInterface/AuthRepositoryInterface.swift b/Projects/Domain/Sources/RepositoryInterface/AuthRepositoryInterface.swift index ef788647..f17b5c83 100644 --- a/Projects/Domain/Sources/RepositoryInterface/AuthRepositoryInterface.swift +++ b/Projects/Domain/Sources/RepositoryInterface/AuthRepositoryInterface.swift @@ -15,8 +15,9 @@ public protocol AuthRepositoryInterface { func kakaoToken() -> Single func appleToken() -> Single func appleButtonTap() -> Single - func oauthSignup(nickname: String, accessToken: String, oauth: OauthType) -> Observable + func oauthSignup(nickname: String, accessToken: String, oauth: OauthType) -> Single func oauthLogin(accessToken: String, oauth: OauthType) -> Single - func oauthRecovery(accessToken: String, oauth: OauthType) -> Observable + func oauthRecovery(accessToken: String, oauth: OauthType) -> Single + func nicknameCheck(nickname: String) -> Single func getIntroData() -> Single } From 2ada90333d15cf437c6310e3af4060852da4946e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=80=ED=98=B8?= <127753071+Eunho0922@users.noreply.github.com> Date: Wed, 27 Mar 2024 09:06:26 +0900 Subject: [PATCH 30/45] =?UTF-8?q?ADD=20::=20[IOS-47]=20=EB=8B=89=EB=84=A4?= =?UTF-8?q?=EC=9E=84=20=EC=A4=91=EB=B3=B5=20=ED=99=95=EC=9D=B8=20=ED=95=A8?= =?UTF-8?q?=EC=88=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 추가 --- .../Sources/Service/AuthService.swift | 46 ++++++++----------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/Projects/Modules/MGNetworks/Sources/Service/AuthService.swift b/Projects/Modules/MGNetworks/Sources/Service/AuthService.swift index 55159788..fec8aca8 100644 --- a/Projects/Modules/MGNetworks/Sources/Service/AuthService.swift +++ b/Projects/Modules/MGNetworks/Sources/Service/AuthService.swift @@ -10,6 +10,7 @@ import DSKit import TokenManager import Domain +import MGLogger import KakaoSDKUser import AuthenticationServices @@ -18,11 +19,17 @@ public class AuthService: NSObject { let kakaoProvider = MoyaProvider() let googleProvider = MoyaProvider() let appleProvider = MoyaProvider() + let authProvider = MoyaProvider() private let keychainAuthorization = KeychainType.authorizationToken private let appleSignupSubject = PublishSubject() + + public func nicknameCheck(nickname: String) -> Single { + return authProvider.rx.request(.nickname(nickname: nickname)) + .filterSuccessfulStatusCodes() + } - public func oauthSingup(nickname: String, accessToken: String, oauth: OauthType) -> Observable { + public func oauthSingup(nickname: String, accessToken: String, oauth: OauthType) -> Single { switch oauth { case .google: return googleSignup(nickname: nickname, accessToken: accessToken) @@ -44,7 +51,7 @@ public class AuthService: NSObject { // } // } - public func oauthLogin(accessToken: String, oauth: OauthType) -> Observable { + public func oauthLogin(accessToken: String, oauth: OauthType) -> Single { switch oauth { case .google: return googleLogin(accessToken: accessToken) @@ -55,7 +62,7 @@ public class AuthService: NSObject { } } - public func oauthRecovery(accessToken: String, oauth: OauthType) -> Observable { + public func oauthRecovery(accessToken: String, oauth: OauthType) -> Single { switch oauth { case .google: return googleRecovery(accessToken: accessToken) @@ -127,7 +134,6 @@ extension AuthService: ASAuthorizationControllerDelegate { let tokenString = String(data: identityToken, encoding: .utf8) { appleSignupSubject.onNext(tokenString) appleSignupSubject.onCompleted() - oauthLogin(accessToken: tokenString, oauth: .apple) } default: break @@ -140,52 +146,40 @@ extension AuthService: ASAuthorizationControllerDelegate { } private extension AuthService { - func googleSignup(nickname: String, accessToken: String) -> Observable { - return googleProvider.rx.request(.googleSignup(nickname: nickname, accessToken: accessToken)).map(SignupResponseDTO.self).asObservable() + func googleSignup(nickname: String, accessToken: String) -> Single { + return googleProvider.rx.request(.googleSignup(nickname: nickname, accessToken: accessToken)) } - func googleLogin(accessToken: String) -> Observable { + func googleLogin(accessToken: String) -> Single { return googleProvider.rx.request(.googleLogin(accessToken: accessToken)) - .map(LoginResponseDTO.self) - .asObservable() } - func googleRecovery(accessToken: String) -> Observable { + func googleRecovery(accessToken: String) -> Single { return googleProvider.rx.request(.googleRecovery(accessToken: accessToken)) - .map(RecoveryResponseDTO.self) - .asObservable() } - func kakaoSignup(nickname: String, accessToken: String) -> Observable { + func kakaoSignup(nickname: String, accessToken: String) -> Single { return kakaoProvider.rx.request(.kakaoSignup(nickname: nickname, accessToken: accessToken)) - .map(SignupResponseDTO.self).asObservable() } - func kakaoLogin(accessToken: String) -> Observable { + func kakaoLogin(accessToken: String) -> Single { return kakaoProvider.rx.request(.kakaoLogin(accessToken: accessToken)) - .map(LoginResponseDTO.self) - .asObservable() } - func kakaoRecovery(accessToken: String) -> Observable { + func kakaoRecovery(accessToken: String) -> Single { return kakaoProvider.rx.request(.kakaoRecovery(accessToken: accessToken)) - .map(RecoveryResponseDTO.self).asObservable() } - func appleSignup(nickname: String, accessToken: String) -> Observable { + func appleSignup(nickname: String, accessToken: String) -> Single { return appleProvider.rx.request(.appleSignup(nickname: nickname, accessToken: accessToken)) - .map(SignupResponseDTO.self).asObservable() } - func appleLogin(accessToken: String) -> Observable { + func appleLogin(accessToken: String) -> Single { return appleProvider.rx.request(.appleLogin(accessToken: accessToken)) - .map(LoginResponseDTO.self) - .asObservable() } - func appleRecovery(accessToken: String) -> Observable { + func appleRecovery(accessToken: String) -> Single { return appleProvider.rx.request(.appleRecovery(accessToken: accessToken)) - .map(RecoveryResponseDTO.self).asObservable() } // public func appleTokenState() -> Single { From 38f235cabe5795738ee9fc35003f3c761a0834ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=80=ED=98=B8?= <127753071+Eunho0922@users.noreply.github.com> Date: Wed, 27 Mar 2024 12:10:05 +0900 Subject: [PATCH 31/45] =?UTF-8?q?ADD=20::=20[IOS-47]=20=EC=9E=84=EC=8B=9C?= =?UTF-8?q?=20=EC=97=90=EB=9F=AC=20=ED=83=80=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/Domain/Sources/Response/LoginDTO.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Projects/Domain/Sources/Response/LoginDTO.swift b/Projects/Domain/Sources/Response/LoginDTO.swift index 79f5028a..9382ff21 100644 --- a/Projects/Domain/Sources/Response/LoginDTO.swift +++ b/Projects/Domain/Sources/Response/LoginDTO.swift @@ -13,3 +13,8 @@ public struct SignupResponseDTO: Decodable { public struct RecoveryResponseDTO: Decodable { public let status: Int } + +enum AuthErrorType: Error { + case notFound400 + case notInt +} From bd5f698de0dcb7e324b1be7b48a2f4cf762dd999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=80=ED=98=B8?= <127753071+Eunho0922@users.noreply.github.com> Date: Wed, 27 Mar 2024 12:10:16 +0900 Subject: [PATCH 32/45] =?UTF-8?q?ADD=20::=20[IOS-47]=20=ED=95=A8=EC=88=98?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/Core/Sources/Base/BaseViewController.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Projects/Core/Sources/Base/BaseViewController.swift b/Projects/Core/Sources/Base/BaseViewController.swift index 7655f9ab..11a87963 100644 --- a/Projects/Core/Sources/Base/BaseViewController.swift +++ b/Projects/Core/Sources/Base/BaseViewController.swift @@ -28,6 +28,7 @@ open class BaseViewController: UIViewController { open override func viewDidLoad() { super.viewDidLoad() bindViewModel() + bindActions() layout() setupKeyboardHandling() attribute() From 1964076dbd1cff44519306365c4a8f7114e6bf51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=80=ED=98=B8?= <127753071+Eunho0922@users.noreply.github.com> Date: Wed, 27 Mar 2024 12:10:41 +0900 Subject: [PATCH 33/45] =?UTF-8?q?ADD=20::=20[IOS-47]=20apple=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 임시 error handling --- .../Domain/Sources/UseCase/AuthUseCase.swift | 144 +++++++++++++----- 1 file changed, 109 insertions(+), 35 deletions(-) diff --git a/Projects/Domain/Sources/UseCase/AuthUseCase.swift b/Projects/Domain/Sources/UseCase/AuthUseCase.swift index 177adbf1..219e0ef8 100644 --- a/Projects/Domain/Sources/UseCase/AuthUseCase.swift +++ b/Projects/Domain/Sources/UseCase/AuthUseCase.swift @@ -15,6 +15,7 @@ public protocol AuthUseCase { func getIntroData() func appleButtonTap() func changeNickname(nickname: String) + func nextButtonTap() var appleSignupResult: PublishSubject { get } var introData: PublishSubject { get } } @@ -40,30 +41,16 @@ extension DefaultAuthUseCase: AuthUseCase { public func changeNickname(nickname: String) { self.nicknameText = nickname } - - public func nextButtonTap() { - let accessToken = TokenManagerImpl().get(key: KeychainType.authorizationToken)! - authRepository.nicknameCheck(nickname: nicknameText) - .subscribe(onSuccess: { [self] element in - MGLogger.debug("nicknameButtonTap nickname ✅ \(element)") - authRepository.oauthSignup(nickname: nicknameText, - accessToken: accessToken, - oauth: self.oauthType) - .subscribe(onSuccess: { [self] element in - MGLogger.debug("nicknameButtonTap Signup ✅ \(element)") - authRepository.oauthLogin(accessToken: accessToken, oauth: self.oauthType) - .subscribe(onSuccess: { element in - MGLogger.debug("nicknameButtonTap Login ✅ \(element)") - AuthStepper.shared.steps.accept(MGStep.authCompleteIsRequired) - }, onFailure: { error in - MGLogger.debug("nicknameButtonTap Login(여기선 절대 에러가 나면 안됩니다...) ❌ \(error)") - }).disposed(by: disposeBag) - }, onFailure: { error in - MGLogger.debug("nicknameButtonTap Signup(여기선 절대 에러가 나면 안됩니다...) ❌ \(error)") - }).disposed(by: disposeBag) + + public func kakaoButtonTap() { + oauthType = .kakao + authRepository.kakaoToken() + .subscribe(onSuccess: { _ in + print("토큰 저장 성공") }, onFailure: { error in - MGLogger.debug("nicknameButtonTap nickname 중복 ❌ \(error)") - }).disposed(by: disposeBag) + print("AuthUseCase getIntroData error occurred: \(error)") + }) + .disposed(by: self.disposeBag) } public func appleButtonTap() { @@ -73,17 +60,58 @@ extension DefaultAuthUseCase: AuthUseCase { MGLogger.debug("appleButtonTap token ✅ \(token)") TokenManagerImpl().save(token: token, with: KeychainType.authorizationToken) authRepository.oauthLogin(accessToken: token, oauth: .apple) + .flatMap { response -> Single in + if response.statusCode >= 400 { + return Single.error(AuthErrorType.notFound400) + } else { + return Single.just(response) + } + } .subscribe(onSuccess: { element in MGLogger.debug("appleButtonTap login ✅ \(String(describing: element.response))") - AuthStepper.shared.steps.accept(MGStep.authCompleteIsRequired) + if let headers = element.response?.headers { + let accessToken = headers.value(for: "Authorization")?.replacingOccurrences(of: "Bearer ", with: "") + let refreshToken = headers["Set-Cookie"]?.components(separatedBy: ";").first(where: { $0.contains("RF-TOKEN") })?.replacingOccurrences(of: "RF-TOKEN=", with: "") + if let accessToken = accessToken { + TokenManagerImpl().save(token: accessToken, with: .authorizationToken) + } + if let refreshToken = refreshToken { + TokenManagerImpl().save(token: refreshToken, with: .refreshToken) + } + } + AuthStepper.shared.steps.accept(MGStep.initialization) }, onFailure: { [self] error in MGLogger.debug("appleButtonTap login ❌ \(error)") authRepository.oauthRecovery(accessToken: token, oauth: .apple) + .flatMap { response -> Single in + if response.statusCode >= 400 { + return Single.error(AuthErrorType.notFound400) + } else { + return Single.just(response) + } + } .subscribe(onSuccess: { [self] element in MGLogger.debug("appleButtonTap recovery ✅ \(element)") authRepository.oauthLogin(accessToken: token, oauth: .apple) + .flatMap { response -> Single in + if response.statusCode >= 400 { + return Single.error(AuthErrorType.notFound400) + } else { + return Single.just(response) + } + } .subscribe(onSuccess: { element in MGLogger.debug("appleButtonTap login ✅ \(String(describing: element.response))") + if let headers = element.response?.headers { + let accessToken = headers.value(for: "Authorization")?.replacingOccurrences(of: "Bearer ", with: "") + let refreshToken = headers["Set-Cookie"]?.components(separatedBy: ";").first(where: { $0.contains("RF-TOKEN") })?.replacingOccurrences(of: "RF-TOKEN=", with: "") + if let accessToken = accessToken { + TokenManagerImpl().save(token: accessToken, with: .authorizationToken) + } + if let refreshToken = refreshToken { + TokenManagerImpl().save(token: refreshToken, with: .refreshToken) + } + } }, onFailure: { error in MGLogger.debug("appleButtonTap login(여기서 절대 에러가 나면 안됩니다...) ❌ \(error)") }).disposed(by: disposeBag) @@ -97,24 +125,70 @@ extension DefaultAuthUseCase: AuthUseCase { }).disposed(by: disposeBag) } + public func nextButtonTap() { + let accessToken = TokenManagerImpl().get(key: KeychainType.authorizationToken)! + authRepository.nicknameCheck(nickname: nicknameText) + .flatMap { response -> Single in + if response.statusCode >= 400 { + return Single.error(AuthErrorType.notFound400) + } else { + return Single.just(response) + } + } + .subscribe(onSuccess: { [self] element in + MGLogger.debug("nicknameButtonTap nickname ✅ \(element)") + authRepository.oauthSignup(nickname: nicknameText, + accessToken: accessToken, + oauth: .apple) + .flatMap { response -> Single in + if response.statusCode >= 400 { + return Single.error(AuthErrorType.notFound400) + } else { + return Single.just(response) + } + } + .subscribe(onSuccess: { [self] element in + MGLogger.debug("nicknameButtonTap Signup ✅ \(element)") + authRepository.oauthLogin(accessToken: accessToken, oauth: .apple) + .flatMap { response -> Single in + if response.statusCode >= 400 { + return Single.error(AuthErrorType.notFound400) + } else { + return Single.just(response) + } + } + .subscribe(onSuccess: { element in + MGLogger.debug("nicknameButtonTap Login ✅ \(element)") + if let headers = element.response?.headers { + let accessToken = headers.value(for: "Authorization") + let refreshToken = headers["Set-Cookie"]?.components(separatedBy: ";").first(where: { $0.contains("RF-TOKEN") })?.replacingOccurrences(of: "RF-TOKEN=", with: "") + if let accessToken = accessToken { + TokenManagerImpl().save(token: accessToken, with: .authorizationToken) + } + if let refreshToken = refreshToken { + TokenManagerImpl().save(token: refreshToken, with: .refreshToken) + } + } + AuthStepper.shared.steps.accept(MGStep.authCompleteIsRequired) + }, onFailure: { error in + MGLogger.debug("nicknameButtonTap Login(여기선 절대 에러가 나면 안됩니다...) ❌ \(error)") + }).disposed(by: disposeBag) + }, onFailure: { error in + MGLogger.debug("nicknameButtonTap Signup(여기선 절대 에러가 나면 안됩니다...) ❌ \(error)") + }).disposed(by: disposeBag) + }, onFailure: { error in + MGLogger.debug("nicknameButtonTap nickname 중복 ❌ \(error)") + }).disposed(by: disposeBag) + } + public func getIntroData() { return authRepository.getIntroData() .subscribe(onSuccess: { [weak self] introModel in self?.introData.onNext(introModel) }, - onFailure: { error in + onFailure: { error in print("AuthUseCase getIntroData error occurred: \(error)") }) .disposed(by: disposeBag) } - - public func kakaoButtonTap() { - return authRepository.kakaoToken() - .subscribe(onSuccess: { _ in - print("토큰 저장 성공") - }, onFailure: { error in - print("AuthUseCase getIntroData error occurred: \(error)") - }) - .disposed(by: self.disposeBag) - } } From 5236a8c00ee4867d5da2f6220faa1595a8198a94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=80=ED=98=B8?= <127753071+Eunho0922@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:25:59 +0900 Subject: [PATCH 34/45] FIX :: [IOS-47] AuthRepository MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit buttonTap 함수로 변경 --- .../Sources/Repository/AuthRepository.swift | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/Projects/Data/Sources/Repository/AuthRepository.swift b/Projects/Data/Sources/Repository/AuthRepository.swift index ab0ad712..f182daed 100644 --- a/Projects/Data/Sources/Repository/AuthRepository.swift +++ b/Projects/Data/Sources/Repository/AuthRepository.swift @@ -8,23 +8,12 @@ import Moya import RxMoya import Domain +import KakaoSDKAuth public class AuthRepository: AuthRepositoryInterface { private let networkService: AuthService - public func googleToken() -> Single { - return networkService.kakaoTokenState() - } - - public func kakaoToken() -> Single { - return networkService.kakaoTokenState() - } - - public func appleToken() -> Single { - return networkService.kakaoTokenState() - } - public func oauthSignup(nickname: String, accessToken: String, oauth: OauthType) -> Single { return networkService.oauthSingup(nickname: nickname, accessToken: accessToken, oauth: oauth) } @@ -48,6 +37,10 @@ public class AuthRepository: AuthRepositoryInterface { public func appleButtonTap() -> Single { return networkService.appleButtonTap() } + + public func kakaoButtonTap() -> Single { + return networkService.kakaoButtonTap() + } public init(networkService: AuthService) { self.networkService = networkService From f3f83b87270630d79600f13459292f05eb9a3ada Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=80=ED=98=B8?= <127753071+Eunho0922@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:26:36 +0900 Subject: [PATCH 35/45] =?UTF-8?q?FIX=20::=20[IOS-47]=20ButtonTap=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RepositoryInterface/AuthRepositoryInterface.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Projects/Domain/Sources/RepositoryInterface/AuthRepositoryInterface.swift b/Projects/Domain/Sources/RepositoryInterface/AuthRepositoryInterface.swift index f17b5c83..e096e0e8 100644 --- a/Projects/Domain/Sources/RepositoryInterface/AuthRepositoryInterface.swift +++ b/Projects/Domain/Sources/RepositoryInterface/AuthRepositoryInterface.swift @@ -4,6 +4,8 @@ import RxSwift import RxCocoa import Moya +import KakaoSDKAuth + public enum OauthType { case google case kakao @@ -11,9 +13,7 @@ public enum OauthType { } public protocol AuthRepositoryInterface { - func googleToken() -> Single - func kakaoToken() -> Single - func appleToken() -> Single + func kakaoButtonTap() -> Single func appleButtonTap() -> Single func oauthSignup(nickname: String, accessToken: String, oauth: OauthType) -> Single func oauthLogin(accessToken: String, oauth: OauthType) -> Single From 6be52c57732fced0bcb6a530c41e8d58a0feeffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=80=ED=98=B8?= <127753071+Eunho0922@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:27:54 +0900 Subject: [PATCH 36/45] ADD :: [IOS-47] AuthUseCase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit kakao Oauth 함수 추가 --- .../Domain/Sources/UseCase/AuthUseCase.swift | 84 ++++++++++++++++--- 1 file changed, 74 insertions(+), 10 deletions(-) diff --git a/Projects/Domain/Sources/UseCase/AuthUseCase.swift b/Projects/Domain/Sources/UseCase/AuthUseCase.swift index 219e0ef8..8b8eb063 100644 --- a/Projects/Domain/Sources/UseCase/AuthUseCase.swift +++ b/Projects/Domain/Sources/UseCase/AuthUseCase.swift @@ -24,7 +24,7 @@ public class DefaultAuthUseCase { private let authRepository: AuthRepositoryInterface private let disposeBag = DisposeBag() - open var oauthType: OauthType! + open var oauthType: OauthType = .kakao public var nicknameText: String = "" public let introData = PublishSubject() @@ -44,13 +44,74 @@ extension DefaultAuthUseCase: AuthUseCase { public func kakaoButtonTap() { oauthType = .kakao - authRepository.kakaoToken() - .subscribe(onSuccess: { _ in - print("토큰 저장 성공") - }, onFailure: { error in - print("AuthUseCase getIntroData error occurred: \(error)") - }) - .disposed(by: self.disposeBag) + authRepository.kakaoButtonTap().subscribe(onSuccess: { [self] token in + let oauthToken = token!.accessToken + MGLogger.debug("kakaoButtonTap token ✅ \(oauthToken)") + TokenManagerImpl().save(token: oauthToken, with: .authorizationToken) + authRepository.oauthLogin(accessToken: oauthToken, oauth: .kakao) + .flatMap { response -> Single in + if response.statusCode >= 400 { + return Single.error(AuthErrorType.notFound400) + } else { + return Single.just(response) + } + } + .subscribe(onSuccess: { element in + MGLogger.debug("appleButtonTap login ✅ \(String(describing: element.response))") + if let headers = element.response?.headers { + let accessToken = headers.value(for: "Authorization")?.replacingOccurrences(of: "Bearer ", with: "") + let refreshToken = headers["Set-Cookie"]?.components(separatedBy: ";").first(where: { $0.contains("RF-TOKEN") })?.replacingOccurrences(of: "RF-TOKEN=", with: "") + if let accessToken = accessToken { + TokenManagerImpl().save(token: accessToken, with: .authorizationToken) + } + if let refreshToken = refreshToken { + TokenManagerImpl().save(token: refreshToken, with: .refreshToken) + } + } + AuthStepper.shared.steps.accept(MGStep.initialization) + }, onFailure: { [self] error in + MGLogger.debug("appleButtonTap login ❌ \(error)") + authRepository.oauthRecovery(accessToken: oauthToken, oauth: .kakao) + .flatMap { response -> Single in + if response.statusCode >= 400 { + return Single.error(AuthErrorType.notFound400) + } else { + return Single.just(response) + } + } + .subscribe(onSuccess: { [self] element in + MGLogger.debug("appleButtonTap recovery ✅ \(element)") + authRepository.oauthLogin(accessToken: oauthToken, oauth: .kakao) + .flatMap { response -> Single in + if response.statusCode >= 400 { + return Single.error(AuthErrorType.notFound400) + } else { + return Single.just(response) + } + } + .subscribe(onSuccess: { element in + MGLogger.debug("appleButtonTap login ✅ \(String(describing: element.response))") + if let headers = element.response?.headers { + let accessToken = headers.value(for: "Authorization")?.replacingOccurrences(of: "Bearer ", with: "") + let refreshToken = headers["Set-Cookie"]?.components(separatedBy: ";").first(where: { $0.contains("RF-TOKEN") })?.replacingOccurrences(of: "RF-TOKEN=", with: "") + if let accessToken = accessToken { + TokenManagerImpl().save(token: accessToken, with: .authorizationToken) + } + if let refreshToken = refreshToken { + TokenManagerImpl().save(token: refreshToken, with: .refreshToken) + } + } + }, onFailure: { error in + MGLogger.debug("appleButtonTap login(여기서 절대 에러가 나면 안됩니다...) ❌ \(error)") + }).disposed(by: disposeBag) + }, onFailure: { error in + MGLogger.debug("appleButtonTap recovery ❌ \(error)") + AuthStepper.shared.steps.accept(MGStep.authAgreeIsRequired) + }).disposed(by: disposeBag) + }).disposed(by: disposeBag) + }, onFailure: { error in + MGLogger.debug("kakaoButtonTap token error ❌ \(error)") + }).disposed(by: disposeBag) } public func appleButtonTap() { @@ -137,10 +198,13 @@ extension DefaultAuthUseCase: AuthUseCase { } .subscribe(onSuccess: { [self] element in MGLogger.debug("nicknameButtonTap nickname ✅ \(element)") + MGLogger.debug("nicknameButtonTap nickname ✅ \(nicknameText)") + authRepository.oauthSignup(nickname: nicknameText, accessToken: accessToken, - oauth: .apple) + oauth: .kakao) .flatMap { response -> Single in + MGLogger.debug(response) if response.statusCode >= 400 { return Single.error(AuthErrorType.notFound400) } else { @@ -149,7 +213,7 @@ extension DefaultAuthUseCase: AuthUseCase { } .subscribe(onSuccess: { [self] element in MGLogger.debug("nicknameButtonTap Signup ✅ \(element)") - authRepository.oauthLogin(accessToken: accessToken, oauth: .apple) + authRepository.oauthLogin(accessToken: accessToken, oauth: .kakao) .flatMap { response -> Single in if response.statusCode >= 400 { return Single.error(AuthErrorType.notFound400) From a2b1b4aedb133010724e01ea1428e4a9b66be4ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=80=ED=98=B8?= <127753071+Eunho0922@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:29:24 +0900 Subject: [PATCH 37/45] =?UTF-8?q?FIX=20::=20[IOS-47]=20rxcocoa=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MaeumGaGymTextField/MG+TextField.swift | 90 +++++++++---------- 1 file changed, 43 insertions(+), 47 deletions(-) diff --git a/Projects/Modules/DSKit/Sources/Components/MaeumGaGymTextField/MG+TextField.swift b/Projects/Modules/DSKit/Sources/Components/MaeumGaGymTextField/MG+TextField.swift index 958e2b49..905ed555 100644 --- a/Projects/Modules/DSKit/Sources/Components/MaeumGaGymTextField/MG+TextField.swift +++ b/Projects/Modules/DSKit/Sources/Components/MaeumGaGymTextField/MG+TextField.swift @@ -1,6 +1,8 @@ import UIKit -import Then + import SnapKit +import Then + import Core import RxSwift import RxCocoa @@ -37,7 +39,7 @@ open class MGTextField: UITextField { errorLabel.text = errorMessage } } - + public var nameErrorType: TextFieldErrorType.Name? { didSet { if let errorType = nameErrorType { @@ -46,17 +48,17 @@ open class MGTextField: UITextField { } } } - + public override var placeholder: String? { didSet { placeholderLabel.text = placeholder super.placeholder = "" } } - + public convenience init(placeholder: String) { self.init(frame: .zero) - + placeholderLabel.text = placeholder } @@ -64,25 +66,24 @@ open class MGTextField: UITextField { super.init(frame: frame) setupUI() } - + required public init?(coder: NSCoder) { super.init(coder: coder) setupUI() } - + private func setupUI() { configure() - - delegate = self + bind() self.tintColor = .black } - + private func configure() { - addSubview(placeholderLabel) - addSubview(underlineView) - addSubview(errorLabel) - + addSubviews([placeholderLabel, + underlineView, + errorLabel]) + placeholderLabel.snp.makeConstraints { $0.leading.trailing.equalToSuperview() $0.bottom.equalTo(underlineView.snp.top).offset(-8.0) @@ -95,45 +96,40 @@ open class MGTextField: UITextField { $0.bottom.equalToSuperview() $0.height.equalTo(1) } - + errorLabel.snp.makeConstraints { $0.leading.trailing.equalToSuperview() $0.top.equalTo(underlineView.snp.bottom).offset(8.0) } - } -} -extension MGTextField: UITextFieldDelegate { - public func textFieldDidBeginEditing(_ textField: UITextField) { - UIView.animate(withDuration: 0.3) { - - self.underlineView.backgroundColor = DSKitAsset.Colors.blue500.color - self.placeholderLabel.font = UIFont.Pretendard.bodySmall - self.placeholderLabel.snp.updateConstraints { - $0.bottom.equalTo(self.underlineView.snp.top).offset(-(self.placeholderLabel.frame.size.height + 4)) - } - self.layoutIfNeeded() - } - } - - public func textFieldDidEndEditing(_ textField: UITextField) { - if textField.text == "" { - UIView.animate(withDuration: 0.3) { - self.underlineView.backgroundColor = DSKitAsset.Colors.gray400.color - self.placeholderLabel.font = UIFont.Pretendard.bodyLarge - self.placeholderLabel.snp.updateConstraints { - $0.bottom.equalTo(self.underlineView.snp.top).offset(-4) + private func bind() { + self.rx.controlEvent(.editingDidBegin) + .asObservable() + .subscribe(onNext: { [weak self] _ in + UIView.animate(withDuration: 0.3) { + self?.underlineView.backgroundColor = DSKitAsset.Colors.blue500.color + self?.placeholderLabel.font = UIFont.Pretendard.bodySmall + self?.placeholderLabel.snp.updateConstraints { + $0.bottom.equalTo(self!.underlineView.snp.top).offset(-(self!.placeholderLabel.frame.size.height + 4)) + } + self?.layoutIfNeeded() } - self.layoutIfNeeded() - } - } - } - - public override func touchesBegan(_ touches: Set, with event: UIEvent?) { - super.touchesBegan(touches, with: event) - if let touch = touches.first { - _ = touch.location(in: self) - } + }).disposed(by: disposeBag) + + self.rx.controlEvent(.editingDidEnd) + .asObservable() + .subscribe(onNext: { [weak self] _ in + if self?.text == "" { + UIView.animate(withDuration: 0.3) { + self?.underlineView.backgroundColor = DSKitAsset.Colors.gray400.color + self?.placeholderLabel.font = UIFont.Pretendard.bodyLarge + self?.placeholderLabel.snp.updateConstraints { + $0.bottom.equalTo(self!.underlineView.snp.top).offset(-4) + } + self?.layoutIfNeeded() + } + } + }).disposed(by: disposeBag) } } From d8ec8cf3068903d739d8d706c25ff93bf57963f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=80=ED=98=B8?= <127753071+Eunho0922@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:30:31 +0900 Subject: [PATCH 38/45] =?UTF-8?q?FIX=20::=20[IOS-47]=20introViewModel=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IntroScene/VC/IntroViewController.swift | 129 +++++------------- 1 file changed, 34 insertions(+), 95 deletions(-) diff --git a/Projects/Features/AuthFeature/Sources/IntroScene/VC/IntroViewController.swift b/Projects/Features/AuthFeature/Sources/IntroScene/VC/IntroViewController.swift index 663d05b6..d6e6e236 100644 --- a/Projects/Features/AuthFeature/Sources/IntroScene/VC/IntroViewController.swift +++ b/Projects/Features/AuthFeature/Sources/IntroScene/VC/IntroViewController.swift @@ -1,8 +1,8 @@ import UIKit -import RxFlow -import RxCocoa import RxSwift +import RxCocoa +import RxFlow import SnapKit import Then @@ -19,9 +19,6 @@ import BaseFeatureDependency import AuthenticationServices public class IntroViewController: BaseViewController, Stepper { - -// let config = GIDConfiguration(clientID: "9435200486-2epc0q27qhose5v9gkjr5vfa7o97md9u.apps.googleusercontent.com") - public var steps = PublishRelay() @@ -38,157 +35,99 @@ public class IntroViewController: BaseViewController, Stepper { font: UIFont.Pretendard.titleMedium, isCenter: true ) - + private var subTitle = MGLabel( font: UIFont.Pretendard.bodyMedium, textColor: DSKitAsset.Colors.gray600.color, numberOfLineCount: 2 ) - + private var stackView = UIStackView().then { $0.axis = .vertical $0.alignment = .center $0.distribution = .equalSpacing $0.spacing = 16.0 } - + private var googleButton = MGAuthButton(type: .google) private var kakaoButton = MGAuthButton(type: .kakao) private var appleButton = MGAuthButton(type: .apple) - + public override func layout() { super.layout() - + let width = view.frame.width / 430.0 let height = view.frame.height / 932.0 - + view.addSubviews([introImageView, mainTitle, subTitle, stackView]) - + stackView.addArrangedSubviews(googleButton, kakaoButton, appleButton) - + introImageView.snp.makeConstraints { $0.centerX.equalToSuperview() - $0.top.equalTo(view.safeAreaLayoutGuide).offset(105.0 * height) + $0.top.equalToSuperview().offset(158.0) } - + mainTitle.snp.makeConstraints { $0.centerX.equalToSuperview() $0.top.equalTo(introImageView.snp.bottom).offset(30.0 * height) } - + subTitle.snp.makeConstraints { $0.centerX.equalToSuperview() $0.top.equalTo(mainTitle.snp.bottom).offset(10.0 * height) } - + googleButton.snp.makeConstraints { $0.centerX.equalToSuperview() $0.width.equalTo(390.0 * width) $0.height.equalTo(60.0 * height) } - + kakaoButton.snp.makeConstraints { $0.centerX.equalToSuperview() $0.width.equalTo(390.0 * width) $0.height.equalTo(60.0 * height) } - + appleButton.snp.makeConstraints { $0.centerX.equalToSuperview() $0.width.equalTo(390.0 * width) $0.height.equalTo(60.0 * height) } - + stackView.snp.makeConstraints { $0.centerX.equalToSuperview() - $0.top.equalTo(subTitle.snp.bottom).offset(20.0 * height) + $0.top.equalTo(subTitle.snp.bottom).offset(68.0) } } - + public override func bindViewModel() { - super.bindViewModel() - + super.bindViewModel() + let useCase = DefaultAuthUseCase(authRepository: AuthRepository(networkService: AuthService())) - viewModel = IntroViewModel(authUseCase: useCase) - - let input = IntroViewModel.Input( - goolgeButtonTapped: googleButton.rx.tap.asDriver(), - appleButtonTapped: appleButton.rx.tap.asDriver(), - kakaoButtonTapped: kakaoButton.rx.tap.asDriver(), - getIntroData: Observable.just(()).asDriver(onErrorDriveWith: .never()) - ) - + viewModel = IntroViewModel(authUseCase: useCase) + + let input = IntroViewModel.Input( + goolgeButtonTapped: googleButton.rx.tap.asDriver(), + appleButtonTapped: appleButton.rx.tap.asDriver(), + kakaoButtonTapped: kakaoButton.rx.tap.asDriver(), + getIntroData: Observable.just(()).asDriver(onErrorDriveWith: .never()) + ) + let ouput = viewModel.transform(input, action: { output in output.introDatas - .subscribe(onNext: { introData in - self.introModel = introData - self.mainTitle.text = self.introModel?.mainTitle - self.subTitle.text = self.introModel?.subTitle + .subscribe(onNext: { [weak self] introData in + self?.introModel = introData + self?.mainTitle.changeText(text: self?.introModel?.mainTitle) + self?.subTitle.changeText(text: self?.introModel?.subTitle) }) .disposed(by: disposeBag) }) - - appleButton.rx.tap - .bind { - let appleProvider = ASAuthorizationAppleIDProvider() - let request = appleProvider.createRequest() - request.requestedScopes = [.fullName, .email] -// - let controller = ASAuthorizationController(authorizationRequests: [request]) - controller.delegate = self - controller.performRequests() - } - .disposed(by: disposeBag) - } -} - -extension IntroViewController: ASAuthorizationControllerDelegate, - ASAuthorizationControllerPresentationContextProviding { - public func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor { - return self.view.window! - } - - public func authorizationController(controller: ASAuthorizationController, - didCompleteWithAuthorization authorization: ASAuthorization - ) { - switch authorization.credential { - case let appleIDCredential as ASAuthorizationAppleIDCredential: - let userIdentifier = appleIDCredential.user - let fullName = appleIDCredential.fullName - let email = appleIDCredential.email - - if let authorizationCode = appleIDCredential.authorizationCode, - let identityToken = appleIDCredential.identityToken, - let authCodeString = String(data: authorizationCode, encoding: .utf8), - let identifyTokenString = String(data: identityToken, encoding: .utf8) { - print("authorizationCode: \(authorizationCode)") - print("identityToken: \(identityToken)") - print("authCodeString: \(authCodeString)") - print("identifyTokenString: \(identifyTokenString)") - } - - print("useridentifier: \(userIdentifier)") - print("fullName: \(String(describing: fullName))") - print("email: \(String(describing: email))") - - case let passwordCredential as ASPasswordCredential: - let username = passwordCredential.user - let password = passwordCredential.password - - print("username: \(username)") - print("password: \(password)") - - default: - break - } - } - - public func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) { - print("login failed - \(error.localizedDescription)") } } From 162dbc33b500498745d98fab4e7407eec5a1d65a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=80=ED=98=B8?= <127753071+Eunho0922@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:31:08 +0900 Subject: [PATCH 39/45] BURN :: [IOS-47] IntroViewModel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 불필요한 코드 변경 --- .../IntroScene/ViewModel/IntroViewModel.swift | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/Projects/Features/AuthFeature/Sources/IntroScene/ViewModel/IntroViewModel.swift b/Projects/Features/AuthFeature/Sources/IntroScene/ViewModel/IntroViewModel.swift index d75c451e..de6ba162 100644 --- a/Projects/Features/AuthFeature/Sources/IntroScene/ViewModel/IntroViewModel.swift +++ b/Projects/Features/AuthFeature/Sources/IntroScene/ViewModel/IntroViewModel.swift @@ -39,10 +39,6 @@ public class IntroViewModel: AuthViewModelType { var introDatas: Observable } -// public var goolgeButtonTap: (() -> Void)? -// public var appleButtonTap: (() -> Void)? -// public var kakaoButtonTap: (() -> Bool)? - private let introModelSubject = PublishSubject() public init(authUseCase: AuthUseCase) { @@ -58,20 +54,8 @@ public class IntroViewModel: AuthViewModelType { bindOutput(output: output) - useCase.getCSRFToken() - .subscribe(onSuccess: { token in - if TokenManagerImpl().save(token: token, with: self.keychainCSRF) { - print("토큰 저장 성공") - } else { - print("토큰 저장 실패") - } - }, onFailure: { error in - print("Error: \(error)") - }) - .disposed(by: disposeBag) - input.goolgeButtonTapped - .drive(onNext: { [weak self] _ in + .drive(onNext: { _ in print("goolgeButtonTapped") AuthStepper.shared.steps.accept(MGStep.authAgreeIsRequired) }) From 2e2d20de1f14a28835524a4426e4160266f23c11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=80=ED=98=B8?= <127753071+Eunho0922@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:31:29 +0900 Subject: [PATCH 40/45] ADD :: [IOS-47] AuthResourcesService MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 이미지 추가 --- .../Sources/ResourcesService/AuthResourcesService.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Projects/Modules/MGNetworks/Sources/ResourcesService/AuthResourcesService.swift b/Projects/Modules/MGNetworks/Sources/ResourcesService/AuthResourcesService.swift index 85610fd8..3530b68b 100644 --- a/Projects/Modules/MGNetworks/Sources/ResourcesService/AuthResourcesService.swift +++ b/Projects/Modules/MGNetworks/Sources/ResourcesService/AuthResourcesService.swift @@ -12,6 +12,7 @@ public enum AuthResourcesService { public static let cancel = DSKitAsset.Assets.circleCancel.image public static let mainLogo = DSKitAsset.Assets.mainLogo.image public static let leftArrow = DSKitAsset.Assets.blackLeftBarArrow.image + public static let introIcon = DSKitAsset.Assets.introIcon.image } public enum Colors { From 2f6d324a543bd92684976a0708fb7dc3324b49a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=80=ED=98=B8?= <127753071+Eunho0922@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:32:52 +0900 Subject: [PATCH 41/45] ENCHANT :: [IOS-47] Auth Service MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 카카오 Oauth 성능개선 코드 간소화 --- .../Sources/Service/AuthService.swift | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/Projects/Modules/MGNetworks/Sources/Service/AuthService.swift b/Projects/Modules/MGNetworks/Sources/Service/AuthService.swift index fec8aca8..5b44cf3c 100644 --- a/Projects/Modules/MGNetworks/Sources/Service/AuthService.swift +++ b/Projects/Modules/MGNetworks/Sources/Service/AuthService.swift @@ -12,6 +12,8 @@ import Domain import MGLogger import KakaoSDKUser +import KakaoSDKAuth + import AuthenticationServices public class AuthService: NSObject { @@ -73,26 +75,18 @@ public class AuthService: NSObject { } } - public func kakaoTokenState() -> Single { - return Single.create { [weak self] single in + public func kakaoButtonTap() -> Single { + return Single.create { single in if UserApi.isKakaoTalkLoginAvailable() { UserApi.shared.loginWithKakaoTalk { (oauthToken, error) in - if let error = error { - single(.success(false)) + if let oauthToken = oauthToken { + single(.success(oauthToken)) } else { - guard let self = self, let accessToken = oauthToken?.accessToken else { - single(.success(false)) - return - } - if TokenManagerImpl().save(token: accessToken, with: self.keychainAuthorization) { - single(.success(true)) - } else { - single(.success(false)) - } + single(.success(nil)) } } } else { - single(.success(false)) + single(.success(nil)) } return Disposables.create() } From 8df048706de3905531c7902e19874009bb8396d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=80=ED=98=B8?= <127753071+Eunho0922@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:33:20 +0900 Subject: [PATCH 42/45] =?UTF-8?q?FIX=20::=20[IOS-47]=20=EC=9E=98=EB=AA=BB?= =?UTF-8?q?=EB=90=9C=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Components/MaeumGaGymProfile/MG+ProfileType.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Projects/Modules/DSKit/Sources/Components/MaeumGaGymProfile/MG+ProfileType.swift b/Projects/Modules/DSKit/Sources/Components/MaeumGaGymProfile/MG+ProfileType.swift index 6e9dafb0..4388958c 100644 --- a/Projects/Modules/DSKit/Sources/Components/MaeumGaGymProfile/MG+ProfileType.swift +++ b/Projects/Modules/DSKit/Sources/Components/MaeumGaGymProfile/MG+ProfileType.swift @@ -61,7 +61,7 @@ public struct MGProfileImage { switch type { case .custom: guard let customImage = customImage else { - return DSKitAsset.Assets.blueHomeTapBar.image + return DSKitAsset.Assets.introIcon.image } return customImage From a5c48cacb9b37793aad961a3e6aabebda7e1b027 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=80=ED=98=B8?= <127753071+Eunho0922@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:33:58 +0900 Subject: [PATCH 43/45] FIX :: [IOS-47] API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit request 방법 변경 --- .../Modules/MGNetworks/Sources/API/Auth/GoogleAPI.swift | 8 +++++--- .../Modules/MGNetworks/Sources/API/Auth/KakaoAPI.swift | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Projects/Modules/MGNetworks/Sources/API/Auth/GoogleAPI.swift b/Projects/Modules/MGNetworks/Sources/API/Auth/GoogleAPI.swift index 3941590d..fb168047 100644 --- a/Projects/Modules/MGNetworks/Sources/API/Auth/GoogleAPI.swift +++ b/Projects/Modules/MGNetworks/Sources/API/Auth/GoogleAPI.swift @@ -39,11 +39,13 @@ extension GoogleAPI: BaseAPI { public var task: Moya.Task { switch self { case let .googleSignup(nickname, accessToken): - let parameters: [String: Any] = [ - "nickname": nickname, + let bodyParameters: [String: Any] = [ + "nickname": nickname + ] + let urlParameters: [String: Any] = [ "access_token": accessToken ] - return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) + return .requestCompositeParameters(bodyParameters: bodyParameters, bodyEncoding: JSONEncoding.default, urlParameters: urlParameters) case let .googleLogin(accessToken): let parameters: [String: Any] = [ "access_token": accessToken diff --git a/Projects/Modules/MGNetworks/Sources/API/Auth/KakaoAPI.swift b/Projects/Modules/MGNetworks/Sources/API/Auth/KakaoAPI.swift index 998b8e2a..ca73cb90 100644 --- a/Projects/Modules/MGNetworks/Sources/API/Auth/KakaoAPI.swift +++ b/Projects/Modules/MGNetworks/Sources/API/Auth/KakaoAPI.swift @@ -39,11 +39,13 @@ extension KakaoAPI: BaseAPI { public var task: Moya.Task { switch self { case let .kakaoSignup(nickname, accessToken): - let parameters: [String: Any] = [ - "nickname": nickname, + let bodyParameters: [String: Any] = [ + "nickname": nickname + ] + let urlParameters: [String: Any] = [ "access_token": accessToken ] - return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) + return .requestCompositeParameters(bodyParameters: bodyParameters, bodyEncoding: JSONEncoding.default, urlParameters: urlParameters) case let .kakaoLogin(accessToken): let parameters: [String: Any] = [ "access_token": accessToken From 104a30dd8cdabb294b3645cf67a6b1786c5082c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=80=ED=98=B8?= <127753071+Eunho0922@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:34:18 +0900 Subject: [PATCH 44/45] FIX :: [IOS-47] Nickname ViewModel --- .../VC/NicknameViewController.swift | 42 +++++--- .../ViewModel/NicknameViewModel.swift | 95 ++++++++----------- 2 files changed, 73 insertions(+), 64 deletions(-) diff --git a/Projects/Features/AuthFeature/Sources/SignupScene/VC/NicknameViewController.swift b/Projects/Features/AuthFeature/Sources/SignupScene/VC/NicknameViewController.swift index 0fb70c84..a3edaab3 100644 --- a/Projects/Features/AuthFeature/Sources/SignupScene/VC/NicknameViewController.swift +++ b/Projects/Features/AuthFeature/Sources/SignupScene/VC/NicknameViewController.swift @@ -114,23 +114,43 @@ public class NicknameViewController: BaseViewController, Step } public override func bindViewModel() { - let navButtonTapped = naviBar.leftButtonTap.asDriver(onErrorDriveWith: .never()) let useCase = DefaultAuthUseCase(authRepository: AuthRepository(networkService: AuthService())) - viewModel = NicknameViewModel(useCase: useCase) + + let navButtonTapped = naviBar.leftButtonTap.asDriver(onErrorDriveWith: .never()) + let nextButtonTapped = checkButton.rx.tap.asDriver(onErrorDriveWith: .never()) + + let input = NicknameViewModel.Input(navButtonTap: navButtonTapped, nextButtonTap: nextButtonTapped) - let input = NicknameViewModel.Input(navButtonTapped: navButtonTapped, nextButtonTap: checkButton.rx.tap.asSignal()) - - let output = viewModel.transform(input, action: { - ouput in - ouput.nextButtonClicked - .drive(onNext: { message in - AuthStepper.shared.steps.accept(MGStep.authCompleteIsRequired) - MGLogger.verbose(message) - }).disposed(by: disposeBag) + let output = viewModel.transform(input, action: { output in + output.nextButtonTap.drive(onNext: { _ in + useCase.changeNickname(nickname: self.nicknameTF.text ?? "") + useCase.nextButtonTap() + }).disposed(by: disposeBag) + + output.navButtonTap.drive(onNext: { _ in + AuthStepper.shared.steps.accept(MGStep.authBack) + }).disposed(by: disposeBag) }) } + public override func bindActions() { + nicknameTF.rx.text + .map { ($0?.count ?? 0) >= 2 && ($0?.count ?? 0) <= 10 ? true : false } + .subscribe( + onNext: { [weak self] state in + MGLogger.debug(state) + switch state { + case true: + self?.checkButton.isEnabled = state + self?.checkButton.backgroundColor = AuthResourcesService.Colors.blue500 + case false: + self?.checkButton.isEnabled = state + self?.checkButton.backgroundColor = AuthResourcesService.Colors.gray400 + } + }).disposed(by: disposeBag) + } + func animateButtonWithKeyboard(notification: NSNotification, show: Bool) { guard let keyboardSize = (notification.userInfo?[ UIResponder.keyboardFrameEndUserInfoKey diff --git a/Projects/Features/AuthFeature/Sources/SignupScene/ViewModel/NicknameViewModel.swift b/Projects/Features/AuthFeature/Sources/SignupScene/ViewModel/NicknameViewModel.swift index 5c31fe43..8ee412d0 100644 --- a/Projects/Features/AuthFeature/Sources/SignupScene/ViewModel/NicknameViewModel.swift +++ b/Projects/Features/AuthFeature/Sources/SignupScene/ViewModel/NicknameViewModel.swift @@ -1,53 +1,42 @@ -//import Foundation -// -//import RxFlow -//import RxCocoa -//import RxSwift -// -//import Core -//import Domain -//import MGNetworks -// -//public class NicknameViewModel: BaseViewModel { -// -// public typealias ViewModel = NicknameViewModel -// -// private let useCase: AuthUseCase -// -// public var disposeBag: DisposeBag = DisposeBag() -// -// public struct Input { -// let navButtonTapped: Driver -// let nextButtonTap: Driver -// } -// -// public struct Output { -// let navButtonTapped: Driver -// let nextButtonClicked: Driver -// } -// -// public init(useCase: AuthUseCase) { -// self.useCase = useCase -// } -// -// public func transform(_ input: Input, action: (Output) -> Void) -> Output { -// -//// let nextButtonClicked = input.nextButtonTap.map { true }.asDriver(onErrorJustReturn: false) -// -// let output = Output(navButtonTapped: input.navButtonTapped.asDriver(), nextButtonClicked: input.nextButtonTap.asDriver()) -// -// action(output) -// -// input.nextButtonTap -// .drive(onNext: { _ in -// useCase.appleNickNameButtonTap() -// }).disposed(by: disposeBag) -// -// input.navButtonTapped -// .drive(onNext: { _ in -// AuthStepper.shared.steps.accept(MGStep.authBack) -// }).disposed(by: disposeBag) -// -// return output -// } -//} +import Foundation + +import RxFlow +import RxCocoa +import RxSwift + +import Core +import Domain +import MGLogger + +import MGNetworks + +public class NicknameViewModel: BaseViewModel { + + public typealias ViewModel = NicknameViewModel + + private let useCase: AuthUseCase + + public var disposeBag: DisposeBag = DisposeBag() + + public struct Input { + let navButtonTap: Driver + let nextButtonTap: Driver + } + + public struct Output { + let navButtonTap: Driver + let nextButtonTap: Driver + } + + public init(useCase: AuthUseCase) { + self.useCase = useCase + } + + public func transform(_ input: Input, action: (Output) -> Void) -> Output { + + let output = Output(navButtonTap: input.navButtonTap.asDriver(), nextButtonTap: input.nextButtonTap.asDriver()) + + action(output) + return output + } +} From 96e38eb5df77e86d81822a5e293e02bcac07c5b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=80=ED=98=B8?= <127753071+Eunho0922@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:34:31 +0900 Subject: [PATCH 45/45] =?UTF-8?q?FIX=20::=20[IOS-47]=20=EA=B0=80=EB=8F=85?= =?UTF-8?q?=EC=84=B1=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/SignupScene/VC/AgreeViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Projects/Features/AuthFeature/Sources/SignupScene/VC/AgreeViewController.swift b/Projects/Features/AuthFeature/Sources/SignupScene/VC/AgreeViewController.swift index 4b3b3b20..4a75d70b 100644 --- a/Projects/Features/AuthFeature/Sources/SignupScene/VC/AgreeViewController.swift +++ b/Projects/Features/AuthFeature/Sources/SignupScene/VC/AgreeViewController.swift @@ -15,7 +15,7 @@ import Domain import MGLogger import MGNetworks -public class AgreeViewController: BaseViewController, UIGestureRecognizerDelegate, Stepper { +public class AgreeViewController: BaseViewController, Stepper, UIGestureRecognizerDelegate { public var steps = PublishRelay()