Skip to content

Commit

Permalink
Merge remote-tracking branch 'refs/remotes/origin/suyeon'
Browse files Browse the repository at this point in the history
Conflicts:
	KkuMulKum/Source/Promise/Tardy/ViewController/TardyViewController.swift
  • Loading branch information
youz2me committed Jul 19, 2024
2 parents cca2c87 + 5f60d2c commit 3792e4a
Show file tree
Hide file tree
Showing 9 changed files with 172 additions and 172 deletions.
29 changes: 12 additions & 17 deletions KkuMulKum/Application/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,13 @@
//

import UIKit

import KakaoSDKAuth

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
let loginViewModel = LoginViewModel()

func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
self.window = UIWindow(windowScene: windowScene)

Expand All @@ -31,19 +26,19 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
}

private func performAutoLogin() {
print("Performing auto login")
loginViewModel.autoLogin { [weak self] success in
DispatchQueue.main.async {
if success {
print("Auto login successful, showing main screen")
self?.showMainScreen()
} else {
print("Auto login failed, showing login screen")
self?.showLoginScreen()
}
print("Performing auto login")
loginViewModel.autoLogin { [weak self] success in
DispatchQueue.main.async {
if success {
print("Auto login successful, showing main screen")
self?.showMainScreen()
} else {
print("Auto login failed, showing login screen")
self?.showLoginScreen()
}
}
}
}

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
if let url = URLContexts.first?.url {
Expand Down Expand Up @@ -73,7 +68,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
}

private func showLoginScreen() {
let loginViewController = LoginViewController()
let loginViewController = LoginViewController(viewModel: loginViewModel)
animateRootViewControllerChange(to: loginViewController)
}

Expand Down
10 changes: 10 additions & 0 deletions KkuMulKum/Network/DTO/Model/Auth/SocialLoginResponseModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,13 @@ struct RefreshTokenResponseModel: ResponseModelType {
let accessToken: String
let refreshToken: String
}

struct UserInfoModel: ResponseModelType {
let userId: Int
let name: String
let level: Int
let promiseCount: Int
let tardyCount: Int
let tardySum: Int
let profileImg: String?
}
2 changes: 0 additions & 2 deletions KkuMulKum/Network/Service/AuthService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
//
// Created by 이지훈 on 7/14/24.
//

import Foundation

import Moya

protocol AuthServiceType {
Expand Down
11 changes: 8 additions & 3 deletions KkuMulKum/Network/TargetType/Auth/AuthTargetType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
//
// Created by 이지훈 on 7/18/24.
//

import Foundation

import Moya

enum AuthTargetType {
Expand All @@ -15,6 +13,7 @@ enum AuthTargetType {
case refreshToken(refreshToken: String)
case updateProfileImage(image: Data, fileName: String, mimeType: String)
case updateName(name: String)
case getUserInfo
}

extension AuthTargetType: TargetType {
Expand All @@ -37,6 +36,8 @@ extension AuthTargetType: TargetType {
return "/api/v1/users/me/image"
case .updateName:
return "/api/v1/users/me/name"
case .getUserInfo:
return "/api/v1/users/me"
}
}

Expand All @@ -46,6 +47,8 @@ extension AuthTargetType: TargetType {
return .post
case .updateProfileImage, .updateName:
return .patch
case .getUserInfo:
return .get
}
}

Expand All @@ -65,6 +68,8 @@ extension AuthTargetType: TargetType {
parameters: ["name": name],
encoding: JSONEncoding.default
)
case .getUserInfo:
return .requestPlain
}
}

Expand All @@ -84,7 +89,7 @@ extension AuthTargetType: TargetType {
"Authorization": "Bearer \(token)",
"Content-Type": "multipart/form-data"
]
case .updateName:
case .updateName, .getUserInfo:
guard let token = DefaultKeychainService.shared.accessToken else {
fatalError("No access token available")
}
Expand Down
8 changes: 7 additions & 1 deletion KkuMulKum/Network/TargetType/MeetingTargetType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,17 @@ extension MeetingTargetType: TargetType {
return .requestJSONEncodable(request)
case .joinMeeting(let request):
return .requestJSONEncodable(request)
case .fetchMeetingList, .fetchMeetingInfo, .fetchMeetingMember, .fetchMeetingPromiseList:
case .fetchMeetingList, .fetchMeetingInfo, .fetchMeetingMember:
return .requestPlain
case .fetchMeetingPromiseList:
return .requestParameters(
parameters: ["done": "false"],
encoding: URLEncoding.queryString
)
}
}


var headers: [String : String]? {
guard let token = DefaultKeychainService.shared.accessToken else {
return ["Content-Type" : "application/json"]
Expand Down
186 changes: 104 additions & 82 deletions KkuMulKum/Source/Onboarding/Login/VIewModel/LoginViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import UIKit
import AuthenticationServices

import KakaoSDKUser
import KakaoSDKAuth
import Moya
Expand All @@ -22,6 +21,7 @@ enum LoginState {
class LoginViewModel: NSObject {
var loginState: ObservablePattern<LoginState> = ObservablePattern(.notLogin)
var error: ObservablePattern<String> = ObservablePattern("")
var userName: ObservablePattern<String?> = ObservablePattern(nil)

private let provider: MoyaProvider<AuthTargetType>
private var authService: AuthServiceType
Expand All @@ -41,7 +41,6 @@ class LoginViewModel: NSObject {
self.keychainAccessible = keychainAccessible
super.init()

// 초기화 시 FCM 토큰 출력
print("Initial FCM Token: \(getFCMToken())")
}

Expand Down Expand Up @@ -111,7 +110,6 @@ class LoginViewModel: NSObject {
}

private func loginToServer(with loginTarget: AuthTargetType) {
// FCM 토큰 출력
switch loginTarget {
case .appleLogin(_, let fcmToken), .kakaoLogin(_, let fcmToken):
print("Sending FCM Token to server: \(fcmToken)")
Expand Down Expand Up @@ -143,76 +141,109 @@ class LoginViewModel: NSObject {
}

private func handleLoginResponse(_ response: ResponseBodyDTO<SocialLoginResponseModel>) {
print("Handling login response")
if response.success, let data = response.data {
saveTokens(
print("Handling login response")
if response.success, let data = response.data {
saveTokens(
accessToken: data.jwtTokenDTO.accessToken,
refreshToken: data.jwtTokenDTO.refreshToken
)
if data.name != nil {
print("Login successful")
loginState.value = .login
} else {
print("Login successful, but needs onboarding.")
loginState.value = .needOnboarding
}
} else {
if let error = response.error {
print("Login failed: \(error.message)")
self.error.value = error.message
} else {
print("Login failed: Unknown error")
self.error.value = "Unknown error occurred"
}
loginState.value = .notLogin
}
}
)
userName.value = data.name
if data.name != nil {
print("Login successful, user has a name")
loginState.value = .login
} else {
print("Login successful, but user needs onboarding")
loginState.value = .needOnboarding
}
} else {
if let error = response.error {
print("Login failed: \(error.message)")
self.error.value = error.message
} else {
print("Login failed: Unknown error")
self.error.value = "Unknown error occurred"
}
loginState.value = .notLogin
}
}

func autoLogin(completion: @escaping (Bool) -> Void) {
guard let refreshToken = authService.getRefreshToken() else {
print("No refresh token found")
loginState.value = .notLogin
completion(false)
return
}

print("Attempting auto login with refresh token")
provider.request(.refreshToken(refreshToken: refreshToken)) { [weak self] result in
switch result {
case .success(let response):
do {
let reissueResponse = try response.map(ResponseBodyDTO<ReissueModel>.self)
if reissueResponse.success, let data = reissueResponse.data {
let newAccessToken = data.accessToken
let newRefreshToken = data.refreshToken
self?.saveTokens(accessToken: newAccessToken, refreshToken: newRefreshToken)
self?.loginState.value = .login
print("Auto login successful")
completion(true)
} else {
print("Token refresh failed: \(reissueResponse.error?.message ?? "Unknown error")")
self?.clearTokensAndHandleError()
completion(false)
}
} catch {
print("Token refresh failed: \(error)")
self?.clearTokensAndHandleError()
completion(false)
}
case .failure(let error):
print("Network error during auto login: \(error)")
self?.clearTokensAndHandleError()
completion(false)
}
}
}

private func clearTokensAndHandleError() {
_ = authService.clearTokens()
loginState.value = .notLogin
error.value = "자동 로그인 실패. 다시 로그인해주세요."
print("Tokens cleared, login state set to notLogin")
}
guard let refreshToken = authService.getRefreshToken() else {
print("No refresh token found")
loginState.value = .notLogin
completion(false)
return
}

print("Attempting auto login with refresh token")
provider.request(.refreshToken(refreshToken: refreshToken)) { [weak self] result in
switch result {
case .success(let response):
do {
let reissueResponse = try response.map(ResponseBodyDTO<RefreshTokenResponseModel>.self)
if reissueResponse.success, let data = reissueResponse.data {
let newAccessToken = data.accessToken
let newRefreshToken = data.refreshToken
self?.saveTokens(accessToken: newAccessToken, refreshToken: newRefreshToken)

self?.fetchUserInfo { success in
if success {
completion(true)
} else {
self?.clearTokensAndHandleError()
completion(false)
}
}
} else {
print("Token refresh failed: \(reissueResponse.error?.message ?? "Unknown error")")
self?.clearTokensAndHandleError()
completion(false)
}
} catch {
print("Token refresh failed: \(error)")
self?.clearTokensAndHandleError()
completion(false)
}
case .failure(let error):
print("Network error during auto login: \(error)")
self?.clearTokensAndHandleError()
completion(false)
}
}
}

private func fetchUserInfo(completion: @escaping (Bool) -> Void) {
provider.request(.getUserInfo) { [weak self] result in
switch result {
case .success(let response):
do {
let userInfoResponse = try response.map(ResponseBodyDTO<UserInfoModel>.self)
if userInfoResponse.success, let data = userInfoResponse.data {
self?.userName.value = data.name
self?.loginState.value = .login // 이름이 있으므로 항상 .login 상태로 설정
completion(true)
} else {
self?.clearTokensAndHandleError()
completion(false)
}
} catch {
print("Failed to decode user info: \(error)")
self?.clearTokensAndHandleError()
completion(false)
}
case .failure(let error):
print("Failed to fetch user info: \(error)")
self?.clearTokensAndHandleError()
completion(false)
}
}
}
private func clearTokensAndHandleError() {
_ = authService.clearTokens()
loginState.value = .notLogin
error.value = "자동 로그인 실패. 다시 로그인해주세요."
print("Tokens cleared, login state set to notLogin")
}

private func saveTokens(accessToken: String, refreshToken: String) {
print("Attempting to save tokens")
Expand All @@ -229,12 +260,8 @@ class LoginViewModel: NSObject {
}
}

extension LoginViewModel: ASAuthorizationControllerDelegate,
ASAuthorizationControllerPresentationContextProviding {
func authorizationController(
controller: ASAuthorizationController,
didCompleteWithAuthorization authorization: ASAuthorization
) {
extension LoginViewModel: ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding {
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
print("Apple authorization completed")
guard let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential,
let identityToken = appleIDCredential.identityToken,
Expand All @@ -249,13 +276,8 @@ extension LoginViewModel: ASAuthorizationControllerDelegate,
}
}

func authorizationController(
controller: ASAuthorizationController,
didCompleteWithError error: Error
) {
print(
"Apple authorization error: \(error.localizedDescription)"
)
func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
print("Apple authorization error: \(error.localizedDescription)")
self.error.value = error.localizedDescription
}

Expand Down
Loading

0 comments on commit 3792e4a

Please sign in to comment.