Skip to content

Commit

Permalink
Merge pull request #214 from OMZigak/feat/#205-MoyaInterceptor
Browse files Browse the repository at this point in the history
[feat] 자동로그인 구현
  • Loading branch information
hooni0918 authored Jul 17, 2024
2 parents c5431b8 + a87ad30 commit 48de49a
Show file tree
Hide file tree
Showing 8 changed files with 256 additions and 89 deletions.
14 changes: 14 additions & 0 deletions KkuMulKum.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
789D73A72C46AF4900C7077D /* KeychainService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 789D73A62C46AF4900C7077D /* KeychainService.swift */; };
789D73AF2C46D99B00C7077D /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 789D73AE2C46D99B00C7077D /* GoogleService-Info.plist */; };
789D73B32C47CC6D00C7077D /* LocalNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 789D73B22C47CC6D00C7077D /* LocalNotificationManager.swift */; };
789D73BE2C47FE0F00C7077D /* AuthInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 789D73BD2C47FE0F00C7077D /* AuthInterceptor.swift */; };
78AED1342C3D951F000AD80A /* NicknameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78AED1332C3D951F000AD80A /* NicknameViewController.swift */; };
78AED1372C3D98D1000AD80A /* NicknameView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78AED1362C3D98D1000AD80A /* NicknameView.swift */; };
78B9286C2C29402C006D9942 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78B9286B2C29402C006D9942 /* AppDelegate.swift */; };
Expand Down Expand Up @@ -245,6 +246,7 @@
789D73AE2C46D99B00C7077D /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
789D73B02C46DACD00C7077D /* KkuMulKum.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = KkuMulKum.entitlements; sourceTree = "<group>"; };
789D73B22C47CC6D00C7077D /* LocalNotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotificationManager.swift; sourceTree = "<group>"; };
789D73BD2C47FE0F00C7077D /* AuthInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthInterceptor.swift; sourceTree = "<group>"; };
78AED1332C3D951F000AD80A /* NicknameViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NicknameViewController.swift; sourceTree = "<group>"; };
78AED1362C3D98D1000AD80A /* NicknameView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NicknameView.swift; sourceTree = "<group>"; };
78B928682C29402C006D9942 /* KkuMulKum.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KkuMulKum.app; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -542,6 +544,14 @@
path = Notification;
sourceTree = "<group>";
};
789D73BC2C47FDEE00C7077D /* Auth */ = {
isa = PBXGroup;
children = (
789D73BD2C47FE0F00C7077D /* AuthInterceptor.swift */,
);
path = Auth;
sourceTree = "<group>";
};
78AED1322C3D9514000AD80A /* Nickname */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -1066,6 +1076,7 @@
DDA2EE7E2C3860B2007C6059 /* Core */ = {
isa = PBXGroup;
children = (
789D73BC2C47FDEE00C7077D /* Auth */,
DDA2EE722C385EB9007C6059 /* MainTabBarController.swift */,
);
path = Core;
Expand Down Expand Up @@ -1744,6 +1755,7 @@
DD3F9DD02C48571A008E1FF7 /* MeetingListServiceType.swift in Sources */,
DE6D4D132C3F14D80005584B /* MeetingMemberCell.swift in Sources */,
DDAF1C932C3D6E3D008A37D3 /* PagePromiseViewController.swift in Sources */,
789D73BE2C47FE0F00C7077D /* AuthInterceptor.swift in Sources */,
789D73B32C47CC6D00C7077D /* LocalNotificationManager.swift in Sources */,
DE9E18922C3BCC9D00DB76B4 /* SocialLoginRequestModel.swift in Sources */,
DE254AA82C3118EA00A4015E /* UIView+.swift in Sources */,
Expand Down Expand Up @@ -1994,6 +2006,7 @@
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = D2DRA3F792;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = KkuMulKum/Resource/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "꾸물꿈";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
Expand Down Expand Up @@ -2027,6 +2040,7 @@
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = D2DRA3F792;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = KkuMulKum/Resource/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "꾸물꿈";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
Expand Down
1 change: 0 additions & 1 deletion KkuMulKum/Application/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
sleep(1) //런치스크린 작동용
// KakaoSDK 초기화 과정에서 앱 키를 동적으로 불러오기
if let kakaoAppKey = fetchKakaoAppKeyFromPrivacyInfo() {
KakaoSDK.initSDK(appKey: kakaoAppKey)
Expand Down
52 changes: 51 additions & 1 deletion KkuMulKum/Application/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
//

import UIKit

import KakaoSDKAuth

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

func scene(
_ scene: UIScene,
Expand All @@ -18,10 +20,31 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
) {
guard let windowScene = (scene as? UIWindowScene) else { return }
self.window = UIWindow(windowScene: windowScene)
self.window?.rootViewController = LoginViewController()

let launchScreenStoryboard = UIStoryboard(name: "LaunchScreen", bundle: nil)
let launchScreenViewController = launchScreenStoryboard.instantiateInitialViewController()

self.window?.rootViewController = launchScreenViewController
self.window?.makeKeyAndVisible()

performAutoLogin()
}

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?.showLoginScreen()
} else {
print("Auto login failed, showing login screen")
self?.showLoginScreen()
}
}
}
}

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
if let url = URLContexts.first?.url {
if (AuthApi.isKakaoTalkLoginUrl(url)) {
Expand All @@ -41,6 +64,33 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
return false
}

private func showMainScreen() {
let mainTabBarController = MainTabBarController()
let navigationController = UINavigationController(rootViewController: mainTabBarController)
navigationController.isNavigationBarHidden = true

animateRootViewControllerChange(to: navigationController)
}

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

private func animateRootViewControllerChange(to newRootViewController: UIViewController) {
guard let window = self.window else { return }

UIView.transition(with: window,
duration: 0.3,
options: .transitionCrossDissolve,
animations: {
let oldState = UIView.areAnimationsEnabled
UIView.setAnimationsEnabled(false)
window.rootViewController = newRootViewController
UIView.setAnimationsEnabled(oldState)
})
}

func sceneDidDisconnect(_ scene: UIScene) {}
func sceneDidBecomeActive(_ scene: UIScene) {}
func sceneWillResignActive(_ scene: UIScene) {}
Expand Down
13 changes: 2 additions & 11 deletions KkuMulKum/Network/TargetType/LoginTargetType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
//
// Created by 이지훈 on 7/15/24.
//

import Foundation

import Moya
Expand All @@ -16,7 +15,6 @@ enum LoginTargetType {
}

extension LoginTargetType: TargetType {

var method: Moya.Method {
.post
}
Expand All @@ -42,15 +40,9 @@ extension LoginTargetType: TargetType {
var task: Task {
switch self {
case let .appleLogin(_, fcmToken):
return .requestParameters(
parameters: ["provider": "APPLE", "fcmToken": fcmToken],
encoding: JSONEncoding.default
)
return .requestJSONEncodable(SocialLoginRequestModel(provider: "APPLE", fcmToken: fcmToken))
case let .kakaoLogin(_, fcmToken):
return .requestParameters(
parameters: ["provider": "KAKAO", "fcmToken": fcmToken],
encoding: JSONEncoding.default
)
return .requestJSONEncodable(SocialLoginRequestModel(provider: "KAKAO", fcmToken: fcmToken))
case .refreshToken:
return .requestPlain
}
Expand All @@ -67,4 +59,3 @@ extension LoginTargetType: TargetType {
}
}
}

78 changes: 78 additions & 0 deletions KkuMulKum/Source/Core/Auth/AuthInterceptor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//
// AuthInterceptor.swift
// KkuMulKum
//
// Created by 이지훈 on 7/17/24.
//

import Foundation

import Moya
import Alamofire

enum AuthError: Error {
case tokenRefreshFailed
}

class AuthInterceptor: RequestInterceptor {
let authService: AuthServiceType
let provider: MoyaProvider<LoginTargetType>

init(authService: AuthServiceType, provider: MoyaProvider<LoginTargetType>) {
self.authService = authService
self.provider = provider
}

func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
guard let accessToken = authService.getAccessToken() else {
completion(.success(urlRequest))
return
}

var urlRequest = urlRequest
urlRequest.addValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
completion(.success(urlRequest))
}

func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
guard let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401 else {
completion(.doNotRetry)
return
}

guard let refreshToken = authService.getRefreshToken() else {
authService.clearTokens()
completion(.doNotRetry)
return
}

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?.authService.saveAccessToken(newAccessToken)
self?.authService.saveRefreshToken(newRefreshToken)
print("Token refreshed successfully in interceptor")
completion(.retry)
} else {
print("Token refresh failed in interceptor: \(reissueResponse.error?.message ?? "Unknown error")")
self?.authService.clearTokens()
completion(.doNotRetry)
}
} catch {
print("Token refresh failed in interceptor: \(error)")
self?.authService.clearTokens()
completion(.doNotRetry)
}
case .failure(let error):
print("Network error during token refresh in interceptor: \(error)")
self?.authService.clearTokens()
completion(.doNotRetry)
}
}
}
}
Loading

0 comments on commit 48de49a

Please sign in to comment.