-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathAuthRepository.swift
More file actions
184 lines (159 loc) · 7.15 KB
/
Copy pathAuthRepository.swift
File metadata and controls
184 lines (159 loc) · 7.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
//
// AuthRepository.swift
// DataSource
//
// Created by 최정인 on 6/30/25.
//
import Domain
import Foundation
import KakaoSDKAuth
import KakaoSDKUser
import Shared
final class AuthRepository: AuthRepositoryProtocol {
private let networkService = NetworkService.shared
private let tokenManager = TokenManager.shared
private let userDefaultsStorage = UserDefaultsStorage.shared
// 카카오 로그인을 진행합니다.
func kakaoLogin() async throws -> UserEntity {
let accessToken = try await fetchKakaoToken()
let (nickname, profileImageUrl) = try await fetchKakaoUserInfo()
let user = try await requestServerLogin(
socialType: .kakao,
nickname: nil,
token: accessToken)
try saveNickname(nickname: nickname)
try saveSocialLoginType(socialLoginType: .kakao)
try saveUserProfileImageUrl(profileImageUrl: profileImageUrl)
return user
}
// 애플 로그인을 진행합니다.
func appleLogin(nickname: String?, authToken: String) async throws -> UserEntity {
var savedNickname: String = ""
if let nickname {
try saveNickname(nickname: nickname)
savedNickname = nickname
} else {
savedNickname = try loadNickname()
}
let user = try await requestServerLogin(
socialType: .apple,
nickname: savedNickname,
token: authToken)
try saveSocialLoginType(socialLoginType: .apple)
return user
}
// 이용 약관 동의를 진행합니다.
func submitAgreement(agreements: [TermsType : Bool]) async throws {
let endpoint = AuthEndpoint.agreements(agreements: agreements)
_ = try await networkService.request(endpoint: endpoint, type: EmptyResponseDTO.self)
}
// 로그아웃을 진행합니다.
func logout() async throws {
let endpoint = AuthEndpoint.logout
_ = try await networkService.request(endpoint: endpoint, type: String.self)
try tokenManager.removeToken()
}
// 탈퇴하기를 진행합니다.
func withdraw() async throws {
let endpoint = AuthEndpoint.withdraw
_ = try await networkService.request(endpoint: endpoint, type: String.self)
try tokenManager.removeToken()
try removeUserInfo()
}
// 카카오 SDK를 통해 카카오 authToken을 받아옵니다.
private func fetchKakaoToken() async throws -> String {
try await withCheckedThrowingContinuation { continuation in
let resultHandler: (OAuthToken?, Error?) -> Void = { oauthToken, error in
if let error {
continuation.resume(throwing: AuthError.unknown(error))
} else if let oauthToken {
continuation.resume(returning: oauthToken.accessToken)
} else {
continuation.resume(throwing: AuthError.kakaoTokenFetchFailed)
}
}
Task { @MainActor in
if UserApi.isKakaoTalkLoginAvailable() {
UserApi.shared.loginWithKakaoTalk(completion: resultHandler)
} else {
UserApi.shared.loginWithKakaoAccount(completion: resultHandler)
}
}
}
}
// 카카오 SDK를 통해 유저의 정보(카카오 닉네임, 프로필 이미지)를 받아옵니다.
private func fetchKakaoUserInfo() async throws -> (nickname: String, profileImageUrl: URL) {
try await withCheckedThrowingContinuation { continuation in
let resultHandler: (User?, Error?) -> Void = { user, error in
if let error {
continuation.resume(throwing: AuthError.unknown(error))
} else if
let nickname = user?.kakaoAccount?.profile?.nickname,
let profileImageUrl = user?.kakaoAccount?.profile?.profileImageUrl {
continuation.resume(returning: (nickname, profileImageUrl))
} else {
continuation.resume(throwing: AuthError.kakaoUserInformationFetchFailed)
}
}
UserApi.shared.me(completion: resultHandler)
}
}
// 서버 로그인을 진행합니다.
private func requestServerLogin(
socialType: SocialLoginType,
nickname: String?,
token: String
) async throws -> UserEntity {
let endpoint = AuthEndpoint.login(
socialLoginType: socialType,
nickname: nickname,
token: token)
guard let userResponse = try await networkService.request(endpoint: endpoint, type: LoginResponseDTO.self)
else { throw AuthError.invalidUserData }
let userEntity = userResponse.toUserEntity()
try tokenManager.saveToken(token: userEntity.accessToken, tokenType: .accessToken)
try tokenManager.saveToken(token: userEntity.refreshToken, tokenType: .refreshToken)
BitnagilLogger.log(logType: .debug, message: "User Logined: \(userEntity.userState)")
BitnagilLogger.log(logType: .debug, message: "AccessToken Saved: \(userEntity.accessToken)")
BitnagilLogger.log(logType: .debug, message: "RefreshToken Saved: \(userEntity.refreshToken)")
return userEntity
}
// UserDefaults에 닉네임을 저장합니다.
private func saveNickname(nickname: String) throws {
guard userDefaultsStorage.save(nickname, forKey: UserDefaultsKey.nickname.rawValue) else {
throw UserError.nicknameSaveFailed
}
}
// UserDefaults에 저장된 닉네임을 불러옵니다.
private func loadNickname() throws -> String {
let nickname: String? = userDefaultsStorage.load(forKey: UserDefaultsKey.nickname.rawValue)
guard let nickname else {
throw UserError.nicknameLoadFailed
}
return nickname
}
// UserDefaults에 소셜 로그인 타입을 저장합니다.
private func saveSocialLoginType(socialLoginType: SocialLoginType) throws {
guard userDefaultsStorage.save(socialLoginType.rawValue, forKey: UserDefaultsKey.socialLoginType.rawValue) else {
throw UserError.socialLoginTypeSaveFailed
}
}
// UserDefaults에 프로필 이미지를 저장합니다.
private func saveUserProfileImageUrl(profileImageUrl: URL) throws {
guard userDefaultsStorage.save(profileImageUrl.absoluteString, forKey: UserDefaultsKey.profileImageUrl.rawValue) else {
throw UserError.profileImageUrlSaveFailed
}
}
// UserDefaults에 저장된 유저 정보(닉네임, 소셜 로그인 타입, 프로필 이미지)를 삭제합니다.
private func removeUserInfo() throws {
guard userDefaultsStorage.remove(forKey: UserDefaultsKey.nickname.rawValue) else {
throw UserError.nicknameRemoveFailed
}
guard userDefaultsStorage.remove(forKey: UserDefaultsKey.socialLoginType.rawValue) else {
throw UserError.socialLoginTypeRemoveFailed
}
guard userDefaultsStorage.remove(forKey: UserDefaultsKey.profileImageUrl.rawValue) else {
throw UserError.profileImageUrlRemoveFailed
}
}
}