// // LoginController.swift // AcaMate // // Created by Sean Kim on 11/26/24. // import Combine import KakaoSDKCommon import KakaoSDKAuth import KakaoSDKUser import Alamofire import Foundation class LoginController { private var cancellables = Set() func login(_ type: SNSLoginType) -> AnyPublisher { switch type { case .Kakao: return self.checkKakaoToken() .handleEvents(receiveCompletion: { completion in switch completion { case .failure(let error) : printLog("KAKAO LOGIN ERROR: \(error)") case .finished: break } }) .eraseToAnyPublisher() // case .Apple: return Fail(error: NSError(domain: "Apple login not implemented", code: 1, userInfo: nil)) .eraseToAnyPublisher() } } func logout(type: SNSLoginType) { switch type { case .Kakao: UserApi.shared.logout { error in if let error = error { printLog(error) } else { printLog("LOGOUT SUCCESS") } } case .Apple: break } } } //MARK: - KAKAO LOGIN extension LoginController { /// 토큰을 가지고 있나 확인 private func checkKakaoToken() -> Future { return Future { promise in // 토큰 확인 if AuthApi.hasToken() { printLog("토큰 있음") self.analysisKakaoToken() .flatMap{ token in self.generateSNSID(token) } .sink { completion in switch completion { case .failure(let error): promise(.failure(error)) case .finished: break } } receiveValue: { data in promise(.success(data)) } .store(in: &self.cancellables) } else { // 처음 설치하거나 하면 여기로 들어오게 됨 -> 당연히 없으니까 바로 printLog("토큰 없음") self.loginKakao() .flatMap { token in self.generateSNSID(token) } .sink { completion in switch completion { case .failure(let error): promise(.failure(error)) case .finished: break } } receiveValue: { data in promise(.success(data)) } .store(in: &self.cancellables) } } } private func generateSNSID(_ token: String) -> Future { return Future { promise in var snsId = SNSID() UserApi.shared.me { user, error in if let error = error { promise(.failure(error)) } else if let user = user, let id = user.id{ snsId.acctType = "kakao" snsId.snsId = "\(id)" snsId.snsToken = "\(token)" if let email = user.kakaoAccount?.email { snsId.snsEmail = email } promise(.success(snsId)) } } } } /// access 토큰의 유효성을 검증 private func analysisKakaoToken() -> Future { return Future { promise in UserApi.shared.accessTokenInfo { tokenInfo, error in if let error = error { printLog("토큰 유효성 체크 실패 - 로그인 동작 필요") if let sdkError = error as? SdkError, sdkError.isInvalidTokenError() == true { // 로그인이 필요 self.loginKakao() // 로그인 후 동작이 sink에서 처리 될것 .sink { completion in switch completion { case .failure(let error): promise(.failure(error)) case .finished: break // 로그 인 후 추가 동작 할 거 있나? 일단 토큰 받아서 만들어 보내는건 Value에서 함 } } receiveValue: { token in printLog("로그인 완료 - 토큰 받아옴 : \(token)") promise(.success(token)) } .store(in: &self.cancellables) } else { // 그 외 기타 에러 promise(.failure(error)) } } else { printLog("토큰 유효성 체크 성공") // 토큰 유효성이 체크 성공, 로그인 불필요 - 해당 토큰으로 카카오 API 호출 가능 if let tokenInfo = tokenInfo, let token = Auth.shared.tokenManager.getToken()?.accessToken { promise(.success(token)) } } } } } private func loginKakao() -> Future { return Future { promise in if (UserApi.isKakaoTalkLoginAvailable()) { // 카카오톡 앱 실행 가능 UserApi.shared.loginWithKakaoTalk { oauthToken, error in if let error = error { // 로그인과정에서 오류 발생 promise(.failure(error)) } else if let oauthToken = oauthToken { // 정상 로그인 완료 promise(.success(oauthToken.accessToken)) } } } else { // 카카오톡 앱 실행 불가 UserApi.shared.loginWithKakaoAccount { oauthToken, error in if let error = error { // 로그인과정에서 오류 발생 promise(.failure(error)) } else if let oauthToken = oauthToken { // 정상 로그인 완료 promise(.success(oauthToken.accessToken)) } } } } } }