66//
77
88import SwiftUI
9+ import Combine
910
1011enum AppRoute {
1112 case splash
@@ -15,66 +16,105 @@ enum AppRoute {
1516}
1617
1718enum DidOnboard {
18- case didOnboard
19+ case didOnboardKakao
20+ case didOnboardApple
1921
2022 var description : String {
2123 switch self {
22- case . didOnboard:
23- return " didOnboard "
24+ case . didOnboardKakao:
25+ return " didOnboardKakao "
26+ case . didOnboardApple:
27+ return " didOnboardApple "
2428 }
2529 }
26-
30+ }
31+
32+ enum SocialType {
33+ case kakao
34+ case apple
2735}
2836
2937final class AppCoordinator : ObservableObject {
3038 @Published var appState : AppRoute = . splash
3139 let tabCoordinator = CertiTabCoordinator ( )
3240 let onboardingCoordinator = OnboardingCoordinator ( )
33-
41+
42+ private var cancellables = Set < AnyCancellable > ( )
43+
3444 init ( ) {
35- // #if DEBUG
36- // TokenManager.shared.clearTokens()
37- // UserDefaults.standard.removeObject(forKey: "DidOnboard.didOnboard.description")
38- // print("[DEBUG] Keychain cleared for login testing")
39- // #endif
45+ // #if DEBUG
46+ // TokenManager.shared.clearTokens()
47+ // UserDefaults.standard.removeObject(forKey: "DidOnboard.didOnboard.description")
48+ // print("[DEBUG] Keychain cleared for login testing")
49+ // #endif
50+ tokenExpiredbind ( )
4051
4152 Task {
4253 await start ( )
4354 }
4455 }
45-
56+
4657 private func start( ) async {
4758 try ? await Task . sleep ( for: . seconds( 2 ) ) // Splash 대기 시간
48-
59+
4960 let tokenResult = TokenManager . shared. getAccessToken ( )
50- let didOnboard = UserDefaults . standard. bool ( forKey: DidOnboard . didOnboard. description)
51-
61+
62+ let isKakaoMember = UserDefaults . standard. bool ( forKey: DidOnboard . didOnboardKakao. description)
63+ let isAppleMember = UserDefaults . standard. bool ( forKey: DidOnboard . didOnboardApple. description)
64+
65+ let isMember = isKakaoMember || isAppleMember
66+
5267 await MainActor . run {
5368 switch tokenResult {
5469 case . success:
55- appState = didOnboard ? . main : . auth
70+ if isMember {
71+ appState = . main
72+ } else {
73+ appState = . auth
74+ }
75+
5676 case . failure:
77+ // 토큰 없음 -> Auth
5778 appState = . auth
5879 }
5980 }
6081 }
61-
62- /// 로그인 완료 시 호출
63- func completeLogin( ) {
64- let didOnboard = UserDefaults . standard. bool ( forKey: DidOnboard . didOnboard. description)
65- appState = didOnboard ? . main : . onboarding
82+
83+ func loginAsExistingUser( type: SocialType ) {
84+ switch type {
85+ case . kakao:
86+ UserDefaults . standard. set ( true , forKey: DidOnboard . didOnboardKakao. description)
87+ case . apple:
88+ UserDefaults . standard. set ( true , forKey: DidOnboard . didOnboardApple. description)
89+ }
90+
91+ appState = . main
6692 }
67-
68- /// 온보딩 완료 시 호출
69- func completeOnboarding( ) {
70- UserDefaults . standard. set ( true , forKey: DidOnboard . didOnboard. description)
93+
94+ /// 신규 유저라서 온보딩이 필요할 때 호출
95+ func goToOnboarding( ) {
96+ appState = . onboarding
97+ }
98+
99+
100+ // MARK: - 온보딩 완료 처리
101+
102+ /// 카카오 유저 온보딩 완료 시 호출
103+ func completeOnboardingKakao( ) {
104+ UserDefaults . standard. set ( true , forKey: DidOnboard . didOnboardKakao. description)
105+ appState = . main
106+ }
107+
108+ /// 애플 유저 온보딩 완료 시 호출
109+ func completeOnboardingApple( ) {
110+ UserDefaults . standard. set ( true , forKey: DidOnboard . didOnboardApple. description)
71111 appState = . main
72112 }
73113
74114 func cancelOnboarding( ) {
75115 appState = . auth
76116 }
77-
117+
78118 /// 로그아웃 시
79119 func logout( ) {
80120 _ = TokenManager . shared. clearTokens ( )
@@ -83,7 +123,24 @@ final class AppCoordinator: ObservableObject {
83123
84124 func withDraw( ) {
85125 _ = TokenManager . shared. clearTokens ( )
86- UserDefaults . standard. removeObject ( forKey: DidOnboard . didOnboard. description)
126+
127+ UserDefaults . standard. removeObject ( forKey: DidOnboard . didOnboardKakao. description)
128+ UserDefaults . standard. removeObject ( forKey: DidOnboard . didOnboardApple. description)
129+
87130 appState = . auth
88131 }
89132}
133+
134+
135+ // MARK: - 리프레쉬 토큰 만료 대응
136+
137+ private extension AppCoordinator {
138+ func tokenExpiredbind( ) {
139+ TokenRefresher . shared. tokenExpiredSubject
140+ . receive ( on: DispatchQueue . main)
141+ . sink { [ weak self] _ in
142+ self ? . logout ( )
143+ }
144+ . store ( in: & cancellables)
145+ }
146+ }
0 commit comments