diff --git a/NADA-iOS-forRelease/Resouces/Constants/Const.swift b/NADA-iOS-forRelease/Resouces/Constants/Const.swift index c37b3728..1b721fb6 100644 --- a/NADA-iOS-forRelease/Resouces/Constants/Const.swift +++ b/NADA-iOS-forRelease/Resouces/Constants/Const.swift @@ -8,5 +8,5 @@ import Foundation struct Const { - + static let headerToken: String = UserDefaults.standard.string(forKey: Const.UserDefaultsKey.accessToken) ?? "" } diff --git a/NADA-iOS-forRelease/Resouces/Constants/Header.swift b/NADA-iOS-forRelease/Resouces/Constants/Header.swift index 44c05e0e..922a9a61 100644 --- a/NADA-iOS-forRelease/Resouces/Constants/Header.swift +++ b/NADA-iOS-forRelease/Resouces/Constants/Header.swift @@ -8,10 +8,11 @@ import Foundation extension Const { + struct Header { static var bearerHeader = ["Authorization": "Bearer " + UserDefaults.standard.string(forKey: Const.UserDefaultsKey.accessToken)!] static var basicHeader = ["Content-Type": "application/json", - "Authorization": "Bearer " + UserDefaults.standard.string(forKey: Const.UserDefaultsKey.accessToken)!] + "Authorization": "Bearer " + headerToken] } } diff --git a/NADA-iOS-forRelease/Resouces/Constants/UserDefaults.swift b/NADA-iOS-forRelease/Resouces/Constants/UserDefaults.swift index f8d09df4..0c9f5af8 100644 --- a/NADA-iOS-forRelease/Resouces/Constants/UserDefaults.swift +++ b/NADA-iOS-forRelease/Resouces/Constants/UserDefaults.swift @@ -16,5 +16,8 @@ extension Const { static let isFirstCard = "isFirstCard" static let isOnboarding = "isOnboarding" static let firstCardID = "firstCardID" + static let isAppleLogin = "isAppleLogin" + static let isKakaoLogin = "isKakaoLogin" + static let hasBeenLaunchedBeforeFlag = "hasBeenLaunchedBeforeFlag" } } diff --git a/NADA-iOS-forRelease/Sources/AppDelegate.swift b/NADA-iOS-forRelease/Sources/AppDelegate.swift index 746a3a45..64580c03 100644 --- a/NADA-iOS-forRelease/Sources/AppDelegate.swift +++ b/NADA-iOS-forRelease/Sources/AppDelegate.swift @@ -7,43 +7,47 @@ import UIKit import KakaoSDKCommon +import KakaoSDKAuth +import KakaoSDKUser import AuthenticationServices @main class AppDelegate: UIResponder, UIApplicationDelegate { - // var isLogin = false + var isLogin = false func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. - // removeKeychainAtFirstLaunch() + + removeTokenAtFirstLaunch() KakaoSDKCommon.initSDK(appKey: "5b8dd8cc878344bb7532eeca4365a4aa") -// let appleIDProvider = ASAuthorizationAppleIDProvider() -// appleIDProvider.getCredentialState(forUserID: Const.UserDefaults.userID) { (credentialState, error) in -// switch credentialState { -// case .authorized: -// print("해당 ID는 연동되어있습니다.") -// self.isLogin = true -// case .revoked: -// print("해당 ID는 연동되어있지않습니다.") -// self.isLogin = false -// case .notFound: -// print("해당 ID를 찾을 수 없습니다.") -// self.isLogin = false -// default: -// break -// } -// } + let acToken = UserDefaults.standard.string(forKey: Const.UserDefaultsKey.accessToken) + let rfToken = UserDefaults.standard.string(forKey: Const.UserDefaultsKey.refreshToken) -// NotificationCenter.default.addObserver(forName: ASAuthorizationAppleIDProvider.credentialRevokedNotification, object: nil, queue: nil) { (Notification) in -// print("Revoked Notification") -// self.isLogin = false -// } + if acToken != "" { + postUserTokenReissue(request: UserTokenReissueRequset(accessToken: acToken ?? "", refreshToken: rfToken ?? "")) + } else { + self.isLogin = false + } + + // 앱 실행 중 애플 ID 강제로 연결 취소 시 + NotificationCenter.default.addObserver(forName: ASAuthorizationAppleIDProvider.credentialRevokedNotification, object: nil, queue: nil) { (Notification) in + print("Revoked Notification") + self.isLogin = false + } return true } + private func removeTokenAtFirstLaunch() { + guard UserDefaults.isFirstLaunch() else { + return + } + UserDefaults.standard.removeObject(forKey: Const.UserDefaultsKey.accessToken) + UserDefaults.standard.removeObject(forKey: Const.UserDefaultsKey.refreshToken) + } + // MARK: UISceneSession Lifecycle func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { @@ -58,6 +62,67 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Use this method to release any resources that were specific to the discarded scenes, as they will not return. } - + private func postUserTokenReissue(request: UserTokenReissueRequset) { + UserAPI.shared.userTokenReissue(request: request) { response in + switch response { + case .success: + print("postUserTokenReissue - Success") + if UserDefaults.standard.bool(forKey: Const.UserDefaultsKey.isAppleLogin) { + // 애플 로그인으로 연동되어 있을 때, -> 애플 ID와의 연동상태 확인 로직 + let appleIDProvider = ASAuthorizationAppleIDProvider() + appleIDProvider.getCredentialState(forUserID: Const.UserDefaultsKey.userID) { (credentialState, error) in + switch credentialState { + case .authorized: + print("해당 ID는 연동되어있습니다.") + self.isLogin = true + case .revoked: + print("해당 ID는 연동되어있지않습니다.") + self.isLogin = false + case .notFound: + print("해당 ID를 찾을 수 없습니다.") + self.isLogin = false + default: + break + } + } + } else { + if AuthApi.hasToken() { // 유효한 토큰 존재 + UserApi.shared.accessTokenInfo { (_, error) in + if let error = error { + if let sdkError = error as? SdkError, sdkError.isInvalidTokenError() == true { + self.isLogin = false + } + } else { + // 토큰 유효성 체크 성공(필요 시 토큰 갱신됨) + self.isLogin = true + } + } + } else { + // 카카오 토큰 없음 -> 로그인 필요 + self.isLogin = false + } + } + case .requestErr(let message): + print("postUserTokenReissue - requestErr: \(message)") + self.isLogin = false + case .pathErr: + print("postUserTokenReissue - pathErr") + case .serverErr: + print("postUserTokenReissue - serverErr") + case .networkFail: + print("postUserTokenReissue - networkFail") + } + } + } } +extension UserDefaults { + public static func isFirstLaunch() -> Bool { + let isFirstLaunch = !UserDefaults.standard.bool(forKey: Const.UserDefaultsKey.hasBeenLaunchedBeforeFlag) + if isFirstLaunch { + UserDefaults.standard.set(true, forKey: Const.UserDefaultsKey.hasBeenLaunchedBeforeFlag) + UserDefaults.standard.synchronize() + } + return isFirstLaunch + } +} diff --git a/NADA-iOS-forRelease/Sources/SceneDelegate.swift b/NADA-iOS-forRelease/Sources/SceneDelegate.swift index f78aa647..15d9ca21 100644 --- a/NADA-iOS-forRelease/Sources/SceneDelegate.swift +++ b/NADA-iOS-forRelease/Sources/SceneDelegate.swift @@ -36,14 +36,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { window.overrideUserInterfaceStyle = .light } } - - // 스플래시 지연시간동안 자동 로그인 작업처리 - DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { - let acToken = self.defaults.string(forKey: Const.UserDefaultsKey.accessToken) - let rfToken = self.defaults.string(forKey: Const.UserDefaultsKey.refreshToken) - - self.postUserTokenReissue(request: UserTokenReissueRequset(accessToken: acToken ?? "", refreshToken: rfToken ?? "")) - } + } func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { @@ -54,53 +47,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } } - // MARK: - Network - - func postUserTokenReissue(request: UserTokenReissueRequset) { - UserAPI.shared.userTokenReissue(request: request) { response in - switch response { - case .success: - print("postUserTokenReissue - Success") - - var rootViewController = UIStoryboard(name: Const.Storyboard.Name.login, bundle: nil) - .instantiateViewController(identifier: Const.ViewController.Identifier.loginViewController) - - if self.defaults.string(forKey: Const.UserDefaultsKey.accessToken) != "" { - rootViewController = UIStoryboard(name: Const.Storyboard.Name.tabBar, bundle: nil).instantiateViewController(withIdentifier: Const.ViewController.Identifier.tabBarViewController) - } else { - rootViewController = UIStoryboard(name: Const.Storyboard.Name.login, bundle: nil) - .instantiateViewController(identifier: Const.ViewController.Identifier.loginViewController) - } - self.window?.rootViewController = rootViewController - self.window?.makeKeyAndVisible() - case .requestErr(let message): - print("postUserTokenReissue - requestErr: \(message)") - - self.presentToLoginViewController() - case .pathErr: - print("postUserTokenReissue - pathErr") - case .serverErr: - print("postUserTokenReissue - serverErr") - case .networkFail: - print("postUserTokenReissue - networkFail") - } - } - } - // MARK: - Methods - private func presentToLoginViewController() { - if UserDefaults.standard.object(forKey: Const.UserDefaultsKey.isOnboarding) != nil { - let rootViewController = UIStoryboard(name: Const.Storyboard.Name.login, bundle: nil).instantiateViewController(identifier: Const.ViewController.Identifier.loginViewController) - self.window?.rootViewController = rootViewController - self.window?.makeKeyAndVisible() - } else { - let rootViewController = UIStoryboard(name: Const.Storyboard.Name.onboarding, bundle: nil).instantiateViewController(identifier: Const.ViewController.Identifier.onboardingViewController) - self.window?.rootViewController = rootViewController - self.window?.makeKeyAndVisible() - } - } - func sceneDidDisconnect(_ scene: UIScene) { // Called as the scene is being released by the system. // This occurs shortly after the scene enters the background, or when its session is discarded. diff --git a/NADA-iOS-forRelease/Sources/ViewControllers/Login/LoginViewController.swift b/NADA-iOS-forRelease/Sources/ViewControllers/Login/LoginViewController.swift index b297f06d..025e35d2 100644 --- a/NADA-iOS-forRelease/Sources/ViewControllers/Login/LoginViewController.swift +++ b/NADA-iOS-forRelease/Sources/ViewControllers/Login/LoginViewController.swift @@ -40,8 +40,6 @@ class LoginViewController: UIViewController { ]) let authorizationButton = ASAuthorizationAppleIDButton(type: .signIn, style: .black) - // let authorizationButton = UIButton() - // authorizationButton.setImage(UIImage(named: "appleLogin"), for: .normal) authorizationButton.addTarget(self, action: #selector(appleSignInButtonPress), for: .touchUpInside) loginProviderStackView.addSubview(authorizationButton) @@ -74,26 +72,21 @@ class LoginViewController: UIViewController { } // 카카오 로그인 버튼 클릭 시 +// @objc + + @objc func kakaoSignInButtonPress() { - if AuthApi.hasToken() { // 유효한 토큰 존재 - UserApi.shared.accessTokenInfo { (_, error) in - if let error = error { - if let sdkError = error as? SdkError, sdkError.isInvalidTokenError() == true { - // 로그인 필요 - self.signUp() - } - } else { - // 토큰 유효성 체크 성공(필요 시 토큰 갱신됨) - self.signUp() - } - } + // 카카오톡 설치 여부 확인 + if UserApi.isKakaoTalkLoginAvailable() { + // 카카오톡 로그인. api 호출 결과를 클로저로 전달. + loginWithApp() } else { - // 카카오 토큰 없음 -> 로그인 필요 - self.signUp() + // 만약, 카카오톡이 깔려있지 않을 경우에는 웹 브라우저로 카카오 로그인함. + loginWithWeb() } } - + // 애플 로그인 버튼 클릭 시 @objc func appleSignInButtonPress() { @@ -124,10 +117,11 @@ extension LoginViewController { } else { if let email = user?.kakaoAccount?.email { self.postUserSignUpWithAPI(request: email) + UserDefaults.standard.set(false, forKey: Const.UserDefaultsKey.isAppleLogin) + UserDefaults.standard.set(true, forKey: Const.UserDefaultsKey.isKakaoLogin) } } } - self.presentToMain() } } @@ -147,10 +141,11 @@ extension LoginViewController { } else { if let email = user?.kakaoAccount?.email { self.postUserSignUpWithAPI(request: email) + UserDefaults.standard.set(false, forKey: Const.UserDefaultsKey.isAppleLogin) + UserDefaults.standard.set(true, forKey: Const.UserDefaultsKey.isKakaoLogin) } } } - self.presentToMain() } } @@ -178,17 +173,13 @@ extension LoginViewController: ASAuthorizationControllerDelegate, ASAuthorizatio // Apple ID 연동 성공 시 func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) { switch authorization.credential { - // Apple ID + // Apple ID case let appleIDCredential as ASAuthorizationAppleIDCredential: let userIdentifier = appleIDCredential.user - // let fullName = appleIDCredential.fullName - // let email = appleIDCredential.email - - // print("User ID : \(userIdentifier)") - // print("User Email : \(email ?? "")") - // print("User Name : \((fullName?.givenName ?? "") + (fullName?.familyName ?? ""))") postUserSignUpWithAPI(request: userIdentifier) + UserDefaults.standard.set(true, forKey: Const.UserDefaultsKey.isAppleLogin) + UserDefaults.standard.set(false, forKey: Const.UserDefaultsKey.isKakaoLogin) presentToMain() default: @@ -211,10 +202,9 @@ extension LoginViewController { print("postUserSignUpWithAPI - success") if let userData = loginData as? UserWithTokenRequest { UserDefaults.standard.set(userData.user.userID, forKey: Const.UserDefaultsKey.userID) - if let tokenData = userData.user.token as? Token { - UserDefaults.standard.set(tokenData.accessToken, forKey: Const.UserDefaultsKey.accessToken) - UserDefaults.standard.set(tokenData.refreshToken, forKey: Const.UserDefaultsKey.refreshToken) - } + UserDefaults.standard.set(userData.user.token.accessToken, forKey: Const.UserDefaultsKey.accessToken) + UserDefaults.standard.set(userData.user.token.refreshToken, forKey: Const.UserDefaultsKey.refreshToken) + } case .requestErr(let message): print("postUserSignUpWithAPI - requestErr: \(message)") diff --git a/NADA-iOS-forRelease/Sources/ViewControllers/Splash/SplashViewController.swift b/NADA-iOS-forRelease/Sources/ViewControllers/Splash/SplashViewController.swift index c2452a6f..a84582c9 100644 --- a/NADA-iOS-forRelease/Sources/ViewControllers/Splash/SplashViewController.swift +++ b/NADA-iOS-forRelease/Sources/ViewControllers/Splash/SplashViewController.swift @@ -11,15 +11,24 @@ class SplashViewController: UIViewController { // MARK: - Properties private weak var appDelegate = UIApplication.shared.delegate as? AppDelegate - - // MARK: - @IBOutlet Properties - + let defaults = UserDefaults.standard + // MARK: - View Life Cycle override func viewDidLoad() { super.viewDidLoad() + + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) - // Do any additional setup after loading the view. - // postUserTokenReissue(request: UserTokenReissueRequset(accessToken: UserDefaults.standard.string(forKey: Const.UserDefaults.accessToken)!, refreshToken: UserDefaults.standard.string(forKey: Const.UserDefaults.refreshToken)!)) + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { + if self.appDelegate?.isLogin == true { + self.presentToMain() + } else { + self.presentToLogin() + } + } } // MARK: - Functions @@ -38,25 +47,3 @@ class SplashViewController: UIViewController { } } - -// MARK: - Networks -extension SplashViewController { - - func postUserTokenReissue(request: UserTokenReissueRequset) { - UserAPI.shared.userTokenReissue(request: request) { response in - switch response { - case .success: - print("postUserTokenReissue - Success") - case .requestErr(let message): - print("postUserTokenReissue - requestErr: \(message)") - self.presentToLogin() - case .pathErr: - print("postUserTokenReissue - pathErr") - case .serverErr: - print("postUserTokenReissue - serverErr") - case .networkFail: - print("postUserTokenReissue - networkFail") - } - } - } -}