From 781c92cfa2dd8f7f6711f5f97da0a75935ac5ab8 Mon Sep 17 00:00:00 2001 From: k906506 Date: Mon, 14 Aug 2023 15:58:00 +0900 Subject: [PATCH 1/8] =?UTF-8?q?add=20#19:=20=EC=B9=B4=EC=B9=B4=EC=98=A4,?= =?UTF-8?q?=20=EC=95=A0=ED=94=8C=20=EC=9D=B4=EB=AF=B8=EC=A7=80=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 --- .../apple_login_white.imageset/Contents.json | 21 ++++++++++++++++++ .../apple_login_white.png | Bin 0 -> 5606 bytes .../kakao_login.imageset/Contents.json | 21 ++++++++++++++++++ .../kakao_login.imageset/kakao_login.png | Bin 0 -> 7213 bytes 4 files changed, 42 insertions(+) create mode 100644 Projects/Keyme/Resources/Assets.xcassets/apple_login_white.imageset/Contents.json create mode 100644 Projects/Keyme/Resources/Assets.xcassets/apple_login_white.imageset/apple_login_white.png create mode 100644 Projects/Keyme/Resources/Assets.xcassets/kakao_login.imageset/Contents.json create mode 100644 Projects/Keyme/Resources/Assets.xcassets/kakao_login.imageset/kakao_login.png diff --git a/Projects/Keyme/Resources/Assets.xcassets/apple_login_white.imageset/Contents.json b/Projects/Keyme/Resources/Assets.xcassets/apple_login_white.imageset/Contents.json new file mode 100644 index 00000000..60958d61 --- /dev/null +++ b/Projects/Keyme/Resources/Assets.xcassets/apple_login_white.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "apple_login_white.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/Keyme/Resources/Assets.xcassets/apple_login_white.imageset/apple_login_white.png b/Projects/Keyme/Resources/Assets.xcassets/apple_login_white.imageset/apple_login_white.png new file mode 100644 index 0000000000000000000000000000000000000000..5f33bcf8d4433d135b600a1630128a55e7ab820c GIT binary patch literal 5606 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 literal 0 HcmV?d00001 From b6476e28d38d52861f9a02c76c044f5c1cc556db Mon Sep 17 00:00:00 2001 From: k906506 Date: Mon, 14 Aug 2023 16:26:25 +0900 Subject: [PATCH 2/8] =?UTF-8?q?feat=20#19:=20=EC=B9=B4=EC=B9=B4=EC=98=A4?= =?UTF-8?q?=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Features/Sources/Root/RootFeature.swift | 17 +++-- .../Sources/SignIn/SignInFeature.swift | 57 +++++++++++++- .../Features/Sources/SignIn/SignInView.swift | 74 ++++++++++++++++++- Projects/Keyme/Sources/KeymeApp.swift | 14 ++++ 4 files changed, 149 insertions(+), 13 deletions(-) diff --git a/Projects/Features/Sources/Root/RootFeature.swift b/Projects/Features/Sources/Root/RootFeature.swift index ca100073..b4ebceb9 100644 --- a/Projects/Features/Sources/Root/RootFeature.swift +++ b/Projects/Features/Sources/Root/RootFeature.swift @@ -3,6 +3,8 @@ // Features // // Created by 이영빈 on 2023/08/10. +// Edited by 고도 on 2023/08/14. +// // Copyright © 2023 team.humanwave. All rights reserved. // @@ -54,14 +56,13 @@ public struct RootFeature: Reducer { Reduce { state, action in switch action { case .login(.presented(let result)): - switch result { - case .succeeded: - localStorage.set(true, forKey: .isLoggedIn) - state.logInStatus = .loggedIn - case .failed: - localStorage.set(false, forKey: .isLoggedIn) - state.logInStatus = .loggedOut - } +// switch result { +// case .succeeded: +// localStorage.set(true, forKey: .isLoggedIn) +// state.logInStatus = .loggedIn +// case .failed: +// localStorage.set(false, forKey: .isLoggedIn) +// state.logInStatus = .loggedOut return .none case .onboarding(.presented(let result)): diff --git a/Projects/Features/Sources/SignIn/SignInFeature.swift b/Projects/Features/Sources/SignIn/SignInFeature.swift index 712f8b08..b1d84300 100644 --- a/Projects/Features/Sources/SignIn/SignInFeature.swift +++ b/Projects/Features/Sources/SignIn/SignInFeature.swift @@ -3,13 +3,22 @@ // Features // // Created by 이영빈 on 2023/08/10. +// Edited by 고도 on 2023/08/14. +// // Copyright © 2023 team.humanwave. All rights reserved. // import Foundation import ComposableArchitecture +import KakaoSDKUser public struct SignInFeature: Reducer { + @Dependency(\.localStorage) private var localStorage + + public enum SignInError: Error { + case noSignIn + } + public enum State: Equatable { case notDetermined case loggedIn @@ -17,13 +26,57 @@ public struct SignInFeature: Reducer { } public enum Action: Equatable { - case succeeded - case failed + case signIn + case signInWithKakaoResponse(TaskResult) +// case succeeded +// case failed } public var body: some Reducer { Reduce { state, action in + switch action { + case .signIn: + return .run { send in + await send(.signInWithKakaoResponse( + TaskResult { + try await signInWithKakao() + } + )) + } + case .signInWithKakaoResponse(.success(true)): // 카카오 로그인 성공 + state = .loggedIn + localStorage.set(true, forKey: .isLoggedIn) + case .signInWithKakaoResponse(.failure): // 카카오 로그인 실패 + state = .loggedOut + default: + state = .loggedOut + } return .none } } + + // FIXME: 토큰 검증 로직 추가 + // 카카오 로그인 메서드 + /// Reducer Closure 내부에서 State를 직접 변경할 수 없어서 Async - Await를 활용하여 한 번 더 이벤트(signInWithKakaoResponse)를 발생시키도록 구현했습니다. + private func signInWithKakao() async throws -> Bool { + return try await withCheckedThrowingContinuation { continuation in + if (UserApi.isKakaoTalkLoginAvailable()) { + UserApi.shared.loginWithKakaoTalk { (_, error) in + if let error = error { + continuation.resume(throwing: SignInError.noSignIn) + } else { + continuation.resume(returning: true) + } + } + } else { + UserApi.shared.loginWithKakaoAccount() { (_, error) in + if let error = error { + continuation.resume(throwing: SignInError.noSignIn) + } else { + continuation.resume(returning: true) + } + } + } + } + } } diff --git a/Projects/Features/Sources/SignIn/SignInView.swift b/Projects/Features/Sources/SignIn/SignInView.swift index 11a2f082..8a15d6af 100644 --- a/Projects/Features/Sources/SignIn/SignInView.swift +++ b/Projects/Features/Sources/SignIn/SignInView.swift @@ -3,13 +3,14 @@ // Keyme // // Created by 이영빈 on 2023/08/10. +// Edited by 고도 on 2023/08/14. +// // Copyright © 2023 team.humanwave. All rights reserved. // import SwiftUI import ComposableArchitecture -// FIXME: Temp public struct SignInView: View { private let store: StoreOf @@ -18,8 +19,75 @@ public struct SignInView: View { } public var body: some View { - Button(action: { store.send(.succeeded) }) { - Text("로그인?") + VStack(alignment: .center, spacing: 0) { + Spacer() + + KakaoLoginButton(store: store) + + AppleLoginButton() + + GuideMessageView() + } + .padding() + } + + // 카카오 로그인 버튼 + struct KakaoLoginButton: View { + let store: StoreOf + + var body: some View { + Button(action: { + store.send(.signIn) + }) { + Image("kakao_login") + .resizable() + .scaledToFill() + } + .frame(width: 312, height: 48) + .cornerRadius(6) + } + } + + // 애플 로그인 버튼 + struct AppleLoginButton: View { + var body: some View { + Button(action: { }) { // FIXME: API 추가 + Image("apple_login_white") + .resizable() + .scaledToFill() + } + .frame(width: 312, height: 48) + .cornerRadius(6) + .padding(.vertical) + } + } + + struct GuideMessageView: View { + var body: some View { + VStack(spacing: 8) { + Text("가입 시, 키미의 다음 사항에 동의하는 것으로 간주합니다.") + .foregroundColor(.gray) + + HStack(spacing: 4) { + Button(action: {}) { + Text("서비스 이용약관") + .fontWeight(.bold) + .foregroundColor(.white) + } + + Text("및") + .foregroundColor(.gray) + + Button(action: {}) { + Text("개인정보 정책") + .fontWeight(.bold) + .foregroundColor(.white) + } + .foregroundColor(.white) + } + } + .font(.system(size: 11)) + .frame(width: 265, height: 36) } } } diff --git a/Projects/Keyme/Sources/KeymeApp.swift b/Projects/Keyme/Sources/KeymeApp.swift index e1a614b5..5f575c85 100644 --- a/Projects/Keyme/Sources/KeymeApp.swift +++ b/Projects/Keyme/Sources/KeymeApp.swift @@ -8,13 +8,27 @@ import FirebaseMessaging import Features import Network +import KakaoSDKAuth +import KakaoSDKCommon + @main struct KeymeApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate + let KAKAO_PRIVATE_KEY = "" // 🚨 SECRET 🚨 + + init() { + KakaoSDK.initSDK(appKey: KAKAO_PRIVATE_KEY) + } + var body: some Scene { WindowGroup { RootView() + .onOpenURL(perform: { url in + if (AuthApi.isKakaoTalkLoginUrl(url)) { + AuthController.handleOpenUrl(url: url) + } + }) } } } From 3fba7380f9b42f718770867eb87fc0bc632d1bc9 Mon Sep 17 00:00:00 2001 From: k906506 Date: Tue, 15 Aug 2023 10:48:33 +0900 Subject: [PATCH 3/8] =?UTF-8?q?feat=20#19:=20=EC=95=A0=ED=94=8C=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Keyme.entitlements | 4 ++ .../Sources/SignIn/SignInFeature.swift | 53 ++++++++++++++-- .../Features/Sources/SignIn/SignInView.swift | 10 ++- .../SignIn/SignInWithAppleDelegate.swift | 63 +++++++++++++++++++ 4 files changed, 121 insertions(+), 9 deletions(-) create mode 100644 Projects/Features/Sources/SignIn/SignInWithAppleDelegate.swift diff --git a/Keyme.entitlements b/Keyme.entitlements index cc78b75e..9ff04609 100644 --- a/Keyme.entitlements +++ b/Keyme.entitlements @@ -4,6 +4,10 @@ aps-environment development + com.apple.developer.applesignin + + Default + com.apple.developer.associated-domains diff --git a/Projects/Features/Sources/SignIn/SignInFeature.swift b/Projects/Features/Sources/SignIn/SignInFeature.swift index b1d84300..cfd937c4 100644 --- a/Projects/Features/Sources/SignIn/SignInFeature.swift +++ b/Projects/Features/Sources/SignIn/SignInFeature.swift @@ -8,13 +8,16 @@ // Copyright © 2023 team.humanwave. All rights reserved. // -import Foundation +import AuthenticationServices import ComposableArchitecture +import Foundation import KakaoSDKUser public struct SignInFeature: Reducer { @Dependency(\.localStorage) private var localStorage + private let signInWithAppleDelegate = SignInWithAppleDelegate() + public enum SignInError: Error { case noSignIn } @@ -26,16 +29,19 @@ public struct SignInFeature: Reducer { } public enum Action: Equatable { - case signIn + case signInWithKakao case signInWithKakaoResponse(TaskResult) -// case succeeded -// case failed + + case signInWithApple + case signInWithAppleResponse(TaskResult) + // case succeeded + // case failed } public var body: some Reducer { Reduce { state, action in switch action { - case .signIn: + case .signInWithKakao: return .run { send in await send(.signInWithKakaoResponse( TaskResult { @@ -43,18 +49,44 @@ public struct SignInFeature: Reducer { } )) } + case .signInWithKakaoResponse(.success(true)): // 카카오 로그인 성공 state = .loggedIn localStorage.set(true, forKey: .isLoggedIn) + return .none + case .signInWithKakaoResponse(.failure): // 카카오 로그인 실패 state = .loggedOut + return .none + + case .signInWithApple: + signInWithApple() + + if signInWithAppleDelegate.isLoggedIn { + return Effect.send(.signInWithAppleResponse(.success(true))) + } else { + return Effect.send(.signInWithAppleResponse(.failure(SignInError.noSignIn))) + } + + case .signInWithAppleResponse(.success(true)): // 애플 로그인 성공 + state = .loggedIn + localStorage.set(true, forKey: .isLoggedIn) + return .none + + case .signInWithAppleResponse(.failure): // 애플 로그인 실패 + state = .loggedOut + return .none + default: state = .loggedOut } + return .none } } - +} + +extension SignInFeature { // FIXME: 토큰 검증 로직 추가 // 카카오 로그인 메서드 /// Reducer Closure 내부에서 State를 직접 변경할 수 없어서 Async - Await를 활용하여 한 번 더 이벤트(signInWithKakaoResponse)를 발생시키도록 구현했습니다. @@ -79,4 +111,13 @@ public struct SignInFeature: Reducer { } } } + + private func signInWithApple() { + let request = ASAuthorizationAppleIDProvider().createRequest() + request.requestedScopes = [.email, .fullName] + + let controller = ASAuthorizationController(authorizationRequests: [request]) + controller.delegate = signInWithAppleDelegate + controller.performRequests() + } } diff --git a/Projects/Features/Sources/SignIn/SignInView.swift b/Projects/Features/Sources/SignIn/SignInView.swift index 8a15d6af..51d1d2ee 100644 --- a/Projects/Features/Sources/SignIn/SignInView.swift +++ b/Projects/Features/Sources/SignIn/SignInView.swift @@ -24,7 +24,7 @@ public struct SignInView: View { KakaoLoginButton(store: store) - AppleLoginButton() + AppleLoginButton(store: store) GuideMessageView() } @@ -37,7 +37,7 @@ public struct SignInView: View { var body: some View { Button(action: { - store.send(.signIn) + store.send(.signInWithKakao) }) { Image("kakao_login") .resizable() @@ -50,8 +50,12 @@ public struct SignInView: View { // 애플 로그인 버튼 struct AppleLoginButton: View { + let store: StoreOf + var body: some View { - Button(action: { }) { // FIXME: API 추가 + Button(action: { + store.send(.signInWithApple) + }) { // FIXME: API 추가 Image("apple_login_white") .resizable() .scaledToFill() diff --git a/Projects/Features/Sources/SignIn/SignInWithAppleDelegate.swift b/Projects/Features/Sources/SignIn/SignInWithAppleDelegate.swift new file mode 100644 index 00000000..efe6644c --- /dev/null +++ b/Projects/Features/Sources/SignIn/SignInWithAppleDelegate.swift @@ -0,0 +1,63 @@ +// +// SignInWithAppleDelegate.swift +// Features +// +// Created by 고도현 on 2023/08/14. +// Copyright © 2023 team.humanwave. All rights reserved. +// + +import AuthenticationServices +import Foundation + +// FIXME: 추후에 폴더 이동 +struct User: Identifiable { + let id: String + let email: String + let name: String +} + +class SignInWithAppleDelegate: NSObject, ASAuthorizationControllerDelegate { + var isLoggedIn = false + + // 애플 로그인에 성공했을 때 + func authorizationController(controller: ASAuthorizationController, + didCompleteWithAuthorization authorization: ASAuthorization) { + switch authorization.credential { + case let appleIdCredential as ASAuthorizationAppleIDCredential: + if let _ = appleIdCredential.email, let _ = appleIdCredential.fullName { + registerNewUser(credential: appleIdCredential) // 1. 애플 로그인으로 처음 시도했을 때 + } else { + signInExistingUser(credential: appleIdCredential) // 2. 기존에 애플 로그인으로 시도한 적이 있을 때 + } + + case let passwordCredential as ASPasswordCredential: + signinWithUserNamePassword(credential: passwordCredential) + + default: + print("[ERROR] 잘못된 접근입니다.") + } + } + + private func signInWithExistAppleAccount(_ credential: ASAuthorizationAppleIDCredential) { + print("[ERROR] 애플 로그인에 실패했습니다.") + + } +} + +// FIXME: API 연결 +extension SignInWithAppleDelegate { + private func registerNewUser(credential: ASAuthorizationAppleIDCredential) { + // API Call - Pass the email, user full name, user identity provided by Apple and other details. + // Give Call Back to UI + } + + private func signInExistingUser(credential: ASAuthorizationAppleIDCredential) { + // API Call - Pass the user identity, authorizationCode and identity token + // Give Call Back to UI + } + + private func signinWithUserNamePassword(credential: ASPasswordCredential) { + // API Call - Sign in with Username and password + // Give Call Back to UI + } +} From f2258206eb7ab795000712aeb96e57f85abe06cc Mon Sep 17 00:00:00 2001 From: enebin Date: Tue, 15 Aug 2023 15:21:16 +0900 Subject: [PATCH 4/8] =?UTF-8?q?add=20#19:=EC=95=A0=ED=94=8C=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=ED=8F=AC=ED=95=A8=ED=95=9C=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=95=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Tuist/Signing/Keyme.DEV.mobileprovision | Bin 14983 -> 19629 bytes Tuist/Signing/Keyme.PROD.mobileprovision | Bin 12328 -> 12496 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Tuist/Signing/Keyme.DEV.mobileprovision b/Tuist/Signing/Keyme.DEV.mobileprovision index de476695a93e4d246b9ed2d036c5c6a4b52c3de1..ea85df2bb5e1b6edece08b7c3b5786470da08981 100644 GIT binary patch delta 5724 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 diff --git a/Tuist/Signing/Keyme.PROD.mobileprovision b/Tuist/Signing/Keyme.PROD.mobileprovision index 7f2b7d096b1b79c4ef3f9df0799c59d498e1b973..79880aae7598a6336d7165f1a56e7c45ce796948 100644 GIT binary patch delta 3534 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 From 137fb71c10f559cd539e3a16eb785dfd761ebb3e Mon Sep 17 00:00:00 2001 From: k906506 Date: Tue, 15 Aug 2023 22:19:04 +0900 Subject: [PATCH 5/8] =?UTF-8?q?feat=20#19:=20=EC=B9=B4=EC=B9=B4=EC=98=A4?= =?UTF-8?q?=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20Keyme=20API=EC=99=80=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/SignIn/SignInFeature.swift | 32 +++++++++-- Projects/Network/Sources/DTO/Auth.swift | 56 +++++++++++++++++++ .../Sources/Network/Foundation/KeymeAPI.swift | 15 +++-- 3 files changed, 94 insertions(+), 9 deletions(-) create mode 100644 Projects/Network/Sources/DTO/Auth.swift diff --git a/Projects/Features/Sources/SignIn/SignInFeature.swift b/Projects/Features/Sources/SignIn/SignInFeature.swift index cfd937c4..8ecc2c73 100644 --- a/Projects/Features/Sources/SignIn/SignInFeature.swift +++ b/Projects/Features/Sources/SignIn/SignInFeature.swift @@ -12,6 +12,7 @@ import AuthenticationServices import ComposableArchitecture import Foundation import KakaoSDKUser +import Network public struct SignInFeature: Reducer { @Dependency(\.localStorage) private var localStorage @@ -58,7 +59,7 @@ public struct SignInFeature: Reducer { case .signInWithKakaoResponse(.failure): // 카카오 로그인 실패 state = .loggedOut return .none - + case .signInWithApple: signInWithApple() @@ -87,13 +88,12 @@ public struct SignInFeature: Reducer { } extension SignInFeature { - // FIXME: 토큰 검증 로직 추가 // 카카오 로그인 메서드 /// Reducer Closure 내부에서 State를 직접 변경할 수 없어서 Async - Await를 활용하여 한 번 더 이벤트(signInWithKakaoResponse)를 발생시키도록 구현했습니다. private func signInWithKakao() async throws -> Bool { return try await withCheckedThrowingContinuation { continuation in if (UserApi.isKakaoTalkLoginAvailable()) { - UserApi.shared.loginWithKakaoTalk { (_, error) in + UserApi.shared.loginWithKakaoTalk { (token, error) in if let error = error { continuation.resume(throwing: SignInError.noSignIn) } else { @@ -101,11 +101,33 @@ extension SignInFeature { } } } else { - UserApi.shared.loginWithKakaoAccount() { (_, error) in + UserApi.shared.loginWithKakaoAccount() { (data, error) in if let error = error { continuation.resume(throwing: SignInError.noSignIn) } else { - continuation.resume(returning: true) + do { + // 1. 카카오 API로 사용자 정보 가져오기 + let jsonData = try JSONEncoder().encode(data) + let parsedData = try JSONDecoder().decode(KakaoOAuthResponse.self, from: jsonData) + + // 2. Keyme API로 사용자 토큰 확인하기 + let auth = KeymeOAuthRequest(oauthType: "KAKAO", token: parsedData.accessToken) + Task { + do { + let result = try await KeymeAPIManager.shared.request(.auth(param: auth), object: KeymeOAuthResponse.self) + + if result.code == 200 { + return continuation.resume(returning: true) + } else { + return continuation.resume(throwing: SignInError.noSignIn) + } + } catch { // 에러가 발생하면 실패 처리 + return continuation.resume(throwing: SignInError.noSignIn) + } + } + } catch { // 에러가 발생하면 실패 처리 + continuation.resume(throwing: SignInError.noSignIn) + } } } } diff --git a/Projects/Network/Sources/DTO/Auth.swift b/Projects/Network/Sources/DTO/Auth.swift new file mode 100644 index 00000000..55f8fdf8 --- /dev/null +++ b/Projects/Network/Sources/DTO/Auth.swift @@ -0,0 +1,56 @@ +// +// Auth.swift +// Network +// +// Created by 고도현 on 2023/08/15. +// Copyright © 2023 team.humanwave. All rights reserved. +// + +import Foundation + +public struct KakaoOAuthResponse: Codable { // 카카오 API Response 관련 Model + public let refreshTokenExpiresIn: Int + public let tokenType: String + public let refreshToken: String + public let accessToken: String + public let expiresIn: Int + public let scope: String + + public init(refreshTokenExpiresIn: Int, tokenType: String, refreshToken: String, accessToken: String, expiresIn: Int, scope: String) { + self.refreshTokenExpiresIn = refreshTokenExpiresIn + self.tokenType = tokenType + self.refreshToken = refreshToken + self.accessToken = accessToken + self.expiresIn = expiresIn + self.scope = scope + } +} + +public struct KeymeOAuthRequest: Codable { // KeyMe API Request 관련 Model + public let oauthType: String + public let token: String + + public init(oauthType: String, token: String) { + self.oauthType = oauthType + self.token = token + } +} + +public struct KeymeOAuthResponse: Codable { // KeyMe API Response 관련 Model + public let code: Int // statusCode + public let data: Data + public let message: String + + public struct Data: Codable { + public let id: Int + public let friendCode: String? + public let nickname: String? + public let profileImage: String? + public let profileTumbnail: String? + public let token: Token + + public struct Token: Codable { + public let accessToken: String + } + } +} diff --git a/Projects/Network/Sources/Network/Foundation/KeymeAPI.swift b/Projects/Network/Sources/Network/Foundation/KeymeAPI.swift index 37b667f3..80c39ef1 100644 --- a/Projects/Network/Sources/Network/Foundation/KeymeAPI.swift +++ b/Projects/Network/Sources/Network/Foundation/KeymeAPI.swift @@ -14,6 +14,7 @@ public enum KeymeAPI { case test case myPage(MyPage) case registerPushToken(String) + case auth(param: KeymeOAuthRequest) } public enum MyPage { @@ -38,9 +39,11 @@ extension KeymeAPI: BaseAPI { return "/members/\(id)/statistics" case .registerPushToken: return "/members/devices" + case .auth: + return "/auth/login" } } - + public var method: Moya.Method { switch self { case .test: @@ -49,9 +52,11 @@ extension KeymeAPI: BaseAPI { return .get case .registerPushToken: return .post + case .auth: + return .post } } - + public var task: Task { switch self { case .test: @@ -60,13 +65,15 @@ extension KeymeAPI: BaseAPI { return .requestParameters(parameters: ["type": type.rawValue], encoding: URLEncoding.default) case .registerPushToken(let token): return .requestParameters(parameters: ["token": token], encoding: JSONEncoding.default) + case .auth(let param): + return .requestJSONEncodable(param) } } - + public var headers: [String: String]? { return ["Content-type": "application/json"] } - + public var sampleData: Data { """ { From 9a030eb91d2214d7ba463590725aad1ba2fa1a55 Mon Sep 17 00:00:00 2001 From: k906506 Date: Wed, 16 Aug 2023 01:56:01 +0900 Subject: [PATCH 6/8] =?UTF-8?q?feat=20#19:=20=EC=95=A0=ED=94=8C=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20Keyme=20API=EC=99=80=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/SignIn/SignInFeature.swift | 49 ++++++++------- .../Features/Sources/SignIn/SignInView.swift | 42 ++++++++++--- .../SignIn/SignInWithAppleDelegate.swift | 63 ------------------- Projects/Network/Sources/DTO/Auth.swift | 23 +++++++ 4 files changed, 85 insertions(+), 92 deletions(-) delete mode 100644 Projects/Features/Sources/SignIn/SignInWithAppleDelegate.swift diff --git a/Projects/Features/Sources/SignIn/SignInFeature.swift b/Projects/Features/Sources/SignIn/SignInFeature.swift index 8ecc2c73..c3d72e52 100644 --- a/Projects/Features/Sources/SignIn/SignInFeature.swift +++ b/Projects/Features/Sources/SignIn/SignInFeature.swift @@ -9,19 +9,20 @@ // import AuthenticationServices +import Combine import ComposableArchitecture import Foundation import KakaoSDKUser import Network +public enum SignInError: Error { + case noSignIn +} + public struct SignInFeature: Reducer { @Dependency(\.localStorage) private var localStorage - private let signInWithAppleDelegate = SignInWithAppleDelegate() - - public enum SignInError: Error { - case noSignIn - } + private var cancellables = Set() public enum State: Equatable { case notDetermined @@ -33,7 +34,7 @@ public struct SignInFeature: Reducer { case signInWithKakao case signInWithKakaoResponse(TaskResult) - case signInWithApple + case signInWithApple(AppleOAuthResponse) case signInWithAppleResponse(TaskResult) // case succeeded // case failed @@ -60,13 +61,13 @@ public struct SignInFeature: Reducer { state = .loggedOut return .none - case .signInWithApple: - signInWithApple() - - if signInWithAppleDelegate.isLoggedIn { - return Effect.send(.signInWithAppleResponse(.success(true))) - } else { - return Effect.send(.signInWithAppleResponse(.failure(SignInError.noSignIn))) + case .signInWithApple(let appleOAuth): + return .run { send in + await send(.signInWithAppleResponse( + TaskResult { + await signInWithApple(appleOAuth) + } + )) } case .signInWithAppleResponse(.success(true)): // 애플 로그인 성공 @@ -111,9 +112,9 @@ extension SignInFeature { let parsedData = try JSONDecoder().decode(KakaoOAuthResponse.self, from: jsonData) // 2. Keyme API로 사용자 토큰 확인하기 - let auth = KeymeOAuthRequest(oauthType: "KAKAO", token: parsedData.accessToken) Task { do { + let auth = KeymeOAuthRequest(oauthType: "KAKAO", token: parsedData.accessToken) let result = try await KeymeAPIManager.shared.request(.auth(param: auth), object: KeymeOAuthResponse.self) if result.code == 200 { @@ -134,12 +135,18 @@ extension SignInFeature { } } - private func signInWithApple() { - let request = ASAuthorizationAppleIDProvider().createRequest() - request.requestedScopes = [.email, .fullName] - - let controller = ASAuthorizationController(authorizationRequests: [request]) - controller.delegate = signInWithAppleDelegate - controller.performRequests() + private func signInWithApple(_ appleOAuth: AppleOAuthResponse) async -> Bool { + do { + let auth = KeymeOAuthRequest(oauthType: "APPLE", token: appleOAuth.identifyToken!) // FIXME: 강제 언래핑 + let result = try await KeymeAPIManager.shared.request(.auth(param: auth), object: KeymeOAuthResponse.self) + + if result.code == 200 { + return true + } else { + return false + } + } catch { + return false + } } } diff --git a/Projects/Features/Sources/SignIn/SignInView.swift b/Projects/Features/Sources/SignIn/SignInView.swift index 51d1d2ee..7ec7fdd3 100644 --- a/Projects/Features/Sources/SignIn/SignInView.swift +++ b/Projects/Features/Sources/SignIn/SignInView.swift @@ -8,8 +8,10 @@ // Copyright © 2023 team.humanwave. All rights reserved. // -import SwiftUI +import AuthenticationServices import ComposableArchitecture +import SwiftUI +import Network public struct SignInView: View { private let store: StoreOf @@ -53,13 +55,37 @@ public struct SignInView: View { let store: StoreOf var body: some View { - Button(action: { - store.send(.signInWithApple) - }) { // FIXME: API 추가 - Image("apple_login_white") - .resizable() - .scaledToFill() - } + SignInWithAppleButton( + onRequest: { request in + request.requestedScopes = [.fullName, .email] + }, + onCompletion: { completion in + switch completion { + case .success(let response): + switch response.credential{ + case let appleIDCredential as ASAuthorizationAppleIDCredential: + let user = appleIDCredential.user + let fullName = appleIDCredential.fullName + let name = (fullName?.familyName ?? "") + (fullName?.givenName ?? "") + let email = appleIDCredential.email + let identifyToken = String(data: appleIDCredential.identityToken!, encoding: .utf8) + let authorizationCode = String(data: appleIDCredential.authorizationCode!, encoding: .utf8) + let appleOAuth = AppleOAuthResponse(user: user, + fullName: fullName, + name: name, + email: email, + identifyToken: identifyToken, + authorizationCode: authorizationCode) + store.send(.signInWithApple(appleOAuth)) + default: + store.send(.signInWithAppleResponse(.failure(SignInError.noSignIn))) + } + case .failure: + store.send(.signInWithAppleResponse(.failure(SignInError.noSignIn))) + } + + }) + .signInWithAppleButtonStyle(.white) .frame(width: 312, height: 48) .cornerRadius(6) .padding(.vertical) diff --git a/Projects/Features/Sources/SignIn/SignInWithAppleDelegate.swift b/Projects/Features/Sources/SignIn/SignInWithAppleDelegate.swift deleted file mode 100644 index efe6644c..00000000 --- a/Projects/Features/Sources/SignIn/SignInWithAppleDelegate.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// SignInWithAppleDelegate.swift -// Features -// -// Created by 고도현 on 2023/08/14. -// Copyright © 2023 team.humanwave. All rights reserved. -// - -import AuthenticationServices -import Foundation - -// FIXME: 추후에 폴더 이동 -struct User: Identifiable { - let id: String - let email: String - let name: String -} - -class SignInWithAppleDelegate: NSObject, ASAuthorizationControllerDelegate { - var isLoggedIn = false - - // 애플 로그인에 성공했을 때 - func authorizationController(controller: ASAuthorizationController, - didCompleteWithAuthorization authorization: ASAuthorization) { - switch authorization.credential { - case let appleIdCredential as ASAuthorizationAppleIDCredential: - if let _ = appleIdCredential.email, let _ = appleIdCredential.fullName { - registerNewUser(credential: appleIdCredential) // 1. 애플 로그인으로 처음 시도했을 때 - } else { - signInExistingUser(credential: appleIdCredential) // 2. 기존에 애플 로그인으로 시도한 적이 있을 때 - } - - case let passwordCredential as ASPasswordCredential: - signinWithUserNamePassword(credential: passwordCredential) - - default: - print("[ERROR] 잘못된 접근입니다.") - } - } - - private func signInWithExistAppleAccount(_ credential: ASAuthorizationAppleIDCredential) { - print("[ERROR] 애플 로그인에 실패했습니다.") - - } -} - -// FIXME: API 연결 -extension SignInWithAppleDelegate { - private func registerNewUser(credential: ASAuthorizationAppleIDCredential) { - // API Call - Pass the email, user full name, user identity provided by Apple and other details. - // Give Call Back to UI - } - - private func signInExistingUser(credential: ASAuthorizationAppleIDCredential) { - // API Call - Pass the user identity, authorizationCode and identity token - // Give Call Back to UI - } - - private func signinWithUserNamePassword(credential: ASPasswordCredential) { - // API Call - Sign in with Username and password - // Give Call Back to UI - } -} diff --git a/Projects/Network/Sources/DTO/Auth.swift b/Projects/Network/Sources/DTO/Auth.swift index 55f8fdf8..e232e1fa 100644 --- a/Projects/Network/Sources/DTO/Auth.swift +++ b/Projects/Network/Sources/DTO/Auth.swift @@ -26,6 +26,29 @@ public struct KakaoOAuthResponse: Codable { // 카카오 API Response 관련 Mod } } +public struct AppleOAuthResponse: Codable, Equatable { // 애플 API Response 관련 Model + public let user: String + public let fullName: PersonNameComponents? + public let name: String + public let email: String? + public let identifyToken: String? + public let authorizationCode: String? + + public init(user: String, + fullName: PersonNameComponents?, + name: String, + email: String?, + identifyToken: String?, + authorizationCode: String?) { + self.user = user + self.fullName = fullName + self.name = name + self.email = email + self.identifyToken = identifyToken + self.authorizationCode = authorizationCode + } +} + public struct KeymeOAuthRequest: Codable { // KeyMe API Request 관련 Model public let oauthType: String public let token: String From 3b2fa09b50141396e9022d2800739be41f9b3536 Mon Sep 17 00:00:00 2001 From: k906506 Date: Wed, 16 Aug 2023 01:58:22 +0900 Subject: [PATCH 7/8] =?UTF-8?q?style=20#19:=20=EC=83=9D=EC=84=B1=EC=9E=90?= =?UTF-8?q?=20=EC=BB=A8=EB=B2=A4=EC=85=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/Network/Sources/DTO/Auth.swift | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Projects/Network/Sources/DTO/Auth.swift b/Projects/Network/Sources/DTO/Auth.swift index e232e1fa..ca034b67 100644 --- a/Projects/Network/Sources/DTO/Auth.swift +++ b/Projects/Network/Sources/DTO/Auth.swift @@ -16,7 +16,12 @@ public struct KakaoOAuthResponse: Codable { // 카카오 API Response 관련 Mod public let expiresIn: Int public let scope: String - public init(refreshTokenExpiresIn: Int, tokenType: String, refreshToken: String, accessToken: String, expiresIn: Int, scope: String) { + public init(refreshTokenExpiresIn: Int, + tokenType: String, + refreshToken: String, + accessToken: String, + expiresIn: Int, + scope: String) { self.refreshTokenExpiresIn = refreshTokenExpiresIn self.tokenType = tokenType self.refreshToken = refreshToken @@ -53,7 +58,8 @@ public struct KeymeOAuthRequest: Codable { // KeyMe API Request 관련 Model public let oauthType: String public let token: String - public init(oauthType: String, token: String) { + public init(oauthType: String, + token: String) { self.oauthType = oauthType self.token = token } From fd23dfd2a6c640e64438494f75b6ba8eb234a44e Mon Sep 17 00:00:00 2001 From: k906506 Date: Wed, 16 Aug 2023 02:02:44 +0900 Subject: [PATCH 8/8] =?UTF-8?q?style=20#19:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C=20=EB=B0=8F?= =?UTF-8?q?=20=EC=BB=A8=EB=B2=A4=EC=85=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/Features/Sources/SignIn/SignInFeature.swift | 3 --- Projects/Features/Sources/SignIn/SignInView.swift | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/Projects/Features/Sources/SignIn/SignInFeature.swift b/Projects/Features/Sources/SignIn/SignInFeature.swift index c3d72e52..d05106f9 100644 --- a/Projects/Features/Sources/SignIn/SignInFeature.swift +++ b/Projects/Features/Sources/SignIn/SignInFeature.swift @@ -9,7 +9,6 @@ // import AuthenticationServices -import Combine import ComposableArchitecture import Foundation import KakaoSDKUser @@ -22,8 +21,6 @@ public enum SignInError: Error { public struct SignInFeature: Reducer { @Dependency(\.localStorage) private var localStorage - private var cancellables = Set() - public enum State: Equatable { case notDetermined case loggedIn diff --git a/Projects/Features/Sources/SignIn/SignInView.swift b/Projects/Features/Sources/SignIn/SignInView.swift index 7ec7fdd3..faba2c76 100644 --- a/Projects/Features/Sources/SignIn/SignInView.swift +++ b/Projects/Features/Sources/SignIn/SignInView.swift @@ -62,11 +62,11 @@ public struct SignInView: View { onCompletion: { completion in switch completion { case .success(let response): - switch response.credential{ + switch response.credential { // FIXME: 추후에 SignInFeature으로 이동 case let appleIDCredential as ASAuthorizationAppleIDCredential: let user = appleIDCredential.user let fullName = appleIDCredential.fullName - let name = (fullName?.familyName ?? "") + (fullName?.givenName ?? "") + let name = (fullName?.familyName ?? "") + (fullName?.givenName ?? "") let email = appleIDCredential.email let identifyToken = String(data: appleIDCredential.identityToken!, encoding: .utf8) let authorizationCode = String(data: appleIDCredential.authorizationCode!, encoding: .utf8)