Skip to content

Commit 21caf54

Browse files
authored
[Merge] #208 - 애플 로그인 구현 및 소셜로그인 타입별 예상되는 사이드 이펙트 대응
2 parents 95d3fb5 + 114e53b commit 21caf54

48 files changed

Lines changed: 372 additions & 130 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CERTI-iOS.xcodeproj/project.pbxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@
288288
buildSettings = {
289289
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
290290
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
291+
CODE_SIGN_ENTITLEMENTS = "CERTI-iOS/CERTI-iOS.entitlements";
291292
CODE_SIGN_STYLE = Automatic;
292293
CURRENT_PROJECT_VERSION = 1;
293294
DEVELOPMENT_TEAM = 42CZFA579G;
@@ -320,6 +321,7 @@
320321
buildSettings = {
321322
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
322323
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
324+
CODE_SIGN_ENTITLEMENTS = "CERTI-iOS/CERTI-iOS.entitlements";
323325
CODE_SIGN_STYLE = Automatic;
324326
CURRENT_PROJECT_VERSION = 1;
325327
DEVELOPMENT_TEAM = 42CZFA579G;

CERTI-iOS/Application/Coordinator/AppCoordinator.swift

Lines changed: 83 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//
77

88
import SwiftUI
9+
import Combine
910

1011
enum AppRoute {
1112
case splash
@@ -15,66 +16,105 @@ enum AppRoute {
1516
}
1617

1718
enum 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

2937
final 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+
}

CERTI-iOS/Application/DIContainer/AppDIContainer.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ final class AppDIContainer {
2727
private lazy var acquisitionService: AcquisitionServiceProtocol = AcquisitionService()
2828
private lazy var careersService: CareersServiceProtocol = CareersService()
2929
private lazy var activityService: ActivityServiceProtocol = ActivityService()
30-
/*private*/ lazy var tokenRefreshService: TokenRefreshServiceProtocol = TokenRefreshService()
3130

3231

3332
// MARK: - Repositories

CERTI-iOS/CERTI-iOS.entitlements

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>com.apple.developer.applesignin</key>
6+
<array>
7+
<string>Default</string>
8+
</array>
9+
</dict>
10+
</plist>

CERTI-iOS/Data/Network/Auth/DTO/Response/LoginResponseDTO.swift

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,27 @@ import Foundation
1010
typealias LoginResponseDTO = BaseResponseDTO<LoginResponseData>
1111

1212
struct LoginResponseData: Codable {
13+
// 최초 로그인 시 응답 DTO
1314
let needSignUp: Bool
14-
let preSignupToken: String
15-
let userInformation: UserInformationData
15+
let preSignupToken: String?
16+
let userInformation: UserInformationData?
17+
let userID: Int?
18+
let nickName: String?
19+
let tokenResponse: TokenResponseData?
1620

1721
enum CodingKeys: String, CodingKey {
18-
case needSignUp, preSignupToken, userInformation
22+
case userID = "userId"
23+
case nickName, needSignUp, preSignupToken, tokenResponse, userInformation
1924
}
2025

2126
func toLoginResponseEntity() -> LoginResponseEntity {
2227
return LoginResponseEntity(
2328
needSignUp: needSignUp,
2429
preSignupToken: preSignupToken,
25-
userInformation: userInformation.toUserInformationEntity()
30+
userInformation: userInformation,
31+
userID: userID,
32+
nickName: nickName,
33+
tokenResponse: tokenResponse
2634
)
2735
}
2836
}

CERTI-iOS/Data/Network/Auth/DTO/UserInformationData.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ import Foundation
99

1010
struct UserInformationData: Codable {
1111
let socialID: String
12-
let name: String?
13-
let socialType, email, profileImageURL: String
12+
let name: String
13+
let socialType, email: String
14+
let profileImageURL: String?
1415

1516
enum CodingKeys: String, CodingKey {
1617
case socialID = "socialId"
@@ -23,7 +24,7 @@ struct UserInformationData: Codable {
2324
socialID: socialID,
2425
socialType: socialType,
2526
email: email,
26-
name: name ?? "",
27+
name: name,
2728
profileImageURL: profileImageURL
2829
)
2930
}

CERTI-iOS/Data/Network/Base/BaseTargetType.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ extension BaseTargetType {
6464

6565
case .refreshTokenHeader:
6666
if case .success(let token) = TokenManager.shared.getRefreshToken() {
67-
headers["Authorization"] = "refreshToken=\(token)"
67+
headers["Authorization"] = "Bearer \(token)"
6868
}
6969
return headers
7070
}

CERTI-iOS/Data/Network/User/DTO/Request/EditProfileRequestDTO.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@ struct EditProfileRequestDTO: Codable {
1717
self.email = entity.email
1818
self.nickName = entity.nickName
1919
self.birthDate = entity.birthDate
20-
self.publicURL = entity.profileImageURL
20+
self.publicURL = entity.profileImageURL ?? ""
2121
}
2222
}

CERTI-iOS/Data/Network/User/DTO/Response/EditProfileResponseDTO.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ typealias EditProfileResponseDTO = BaseResponseDTO<EditProfileResponseData>
1212
struct EditProfileResponseData: Codable {
1313
let nickName, name, email: String
1414
let birthDate: String?
15-
let profileImageURL: String
15+
let profileImageURL: String?
1616

1717
func toEditProfileEntity() -> EditProfileEntity {
1818
return EditProfileEntity(

CERTI-iOS/Data/Network/User/DTO/Response/MyPageResponseDTO.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,16 @@ import Foundation
1010
typealias MyPageResponseDTO = BaseResponseDTO<MyPageResponseData>
1111

1212
struct MyPageResponseData: Codable {
13-
let nickname, profileImageURL, email: String
13+
let nickname, email: String
14+
let profileImageURL: String?
1415
let jobResponse: JobResponseData
1516
let upCount, acCount, fCount: Int
1617

1718
func toMyPageEntity() -> MyPageEntity {
1819
return MyPageEntity(
1920
nickname: nickname,
20-
profileImageURL: profileImageURL,
2121
email: email,
22+
profileImageURL: profileImageURL,
2223
jobResponse: jobResponse.toJobEntity(),
2324
upCount: upCount,
2425
acCount: acCount,

0 commit comments

Comments
 (0)