Skip to content

Commit ea0ff26

Browse files
authored
더보기 탭 1차 (#338)
* Add `Settings` module * Add `SettingsMenuItem` * Add `MyAccountScene` * Add `TimetableSettingView` * Add device & logout * Add `AuthUseCase` protocol * Delete account * Fix typo * Apply SwiftFormat changes * Fix typo (review) * Extract `syncAuthState()` * Initialize KakaoMapSDK at AppDelegate * Apply SwiftFormat changes * Remove `NavigationLink` * Apply SwiftFormat changes --------- Co-authored-by: peng-u-0807 <peng-u-0807@users.noreply.github.com>
1 parent b5bcc72 commit ea0ff26

Some content is hidden

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

41 files changed

+1356
-56
lines changed

.gitignore

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,6 @@ DerivedData
2626
#
2727
Pods/
2828

29-
# SNUTT-iOS
30-
SNUTT/config.plist
31-
SNUTT/GoogleService-Info-Dev.plist
32-
SNUTT/GoogleService-Info-Production.plist
33-
3429
# SNUTT-iOS-2022
3530
SNUTT-2022/SNUTT/*.xcconfig
3631
SNUTT-2022/SNUTT/GoogleService*.plist

SNUTT/.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,8 @@ graph.dot
6868
Derived/
6969

7070
### Tuist managed dependencies ###
71-
Tuist/.build
71+
Tuist/.build
72+
73+
### Firebase ###
74+
SNUTT/Resources/GoogleServiceDebugInfo.plist
75+
SNUTT/Resources/GoogleServiceReleaseInfo.plist
Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
//
32
// AuthUseCase.swift
43
// SNUTT
@@ -9,26 +8,54 @@
98
import AuthInterface
109
import Dependencies
1110

12-
struct AuthUseCase {
11+
public struct AuthUseCase: AuthUseCaseProtocol {
1312
@Dependency(\.authRepository) private var authRepository
1413
@Dependency(\.authState) private var authState
1514
@Dependency(\.authSecureRepository) private var secureRepository
1615

17-
func loginWithLocalID(localID: String, localPassword: String) async throws {
16+
public init() {}
17+
18+
public func syncAuthState() {
19+
if let userID = authState.get(.userID) {
20+
// If userID exists in userDefaults, store it into in-memory store
21+
authState.set(.userID, value: userID)
22+
} else {
23+
// The user has never logged in, clear the keychain
24+
authState.clear()
25+
try? secureRepository.clear()
26+
return
27+
}
28+
if let accessToken = secureRepository.getAccessToken() {
29+
authState.set(.accessToken, value: accessToken)
30+
}
31+
}
32+
33+
public func loginWithLocalID(localID: String, localPassword: String) async throws {
1834
let response = try await authRepository.loginWithLocalID(localID: localID, localPassword: localPassword)
1935
try secureRepository.saveAccessToken(response.accessToken)
2036
authState.set(.accessToken, value: response.accessToken)
2137
authState.set(.userID, value: response.userID)
2238
}
23-
}
2439

25-
extension AuthUseCase: DependencyKey {
26-
static let liveValue: AuthUseCase = .init()
27-
}
40+
public func logout() async throws {
41+
guard let fcmToken = authState.get(.fcmToken) else {
42+
return
43+
}
44+
try await authRepository.logout(fcmToken: fcmToken)
45+
try secureRepository.clear()
46+
authState.clear()
47+
}
48+
49+
public func registerFCMToken(_ token: String) async throws {
50+
authState.set(.fcmToken, value: token)
51+
// register fcm token only when `accessToken` exists
52+
guard let _ = authState.get(.accessToken) else { return }
53+
try await authRepository.addDevice(fcmToken: token)
54+
}
2855

29-
extension DependencyValues {
30-
var authUseCase: AuthUseCase {
31-
get { self[AuthUseCase.self] }
32-
set { self[AuthUseCase.self] = newValue }
56+
public func deleteAccount() async throws {
57+
try await authRepository.deleteAccount()
58+
try secureRepository.clear()
59+
authState.clear()
3360
}
3461
}

SNUTT/Modules/Feature/Auth/Sources/Infra/AuthAPIRepository.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ public struct AuthAPIRepository: AuthRepository {
1414

1515
public init() {}
1616

17-
public func registerWithLocalID(localID: String, localPassword: String, email _: String) async throws -> LoginResponse {
17+
public func registerWithLocalID(localID: String, localPassword: String,
18+
email _: String) async throws -> LoginResponse
19+
{
1820
let result = try await apiClient.loginLocal(body: .json(.init(id: localID, password: localPassword)))
1921
let json = try result.ok.body.json
2022
return .init(accessToken: json.token, userID: json.user_id)
@@ -25,4 +27,16 @@ public struct AuthAPIRepository: AuthRepository {
2527
let json = try result.ok.body.json
2628
return .init(accessToken: json.token, userID: json.user_id)
2729
}
30+
31+
public func addDevice(fcmToken: String) async throws {
32+
try await apiClient.registerLocal_1(path: .init(id: fcmToken))
33+
}
34+
35+
public func logout(fcmToken: String) async throws {
36+
try await apiClient.logout(body: .json(.init(registration_id: fcmToken)))
37+
}
38+
39+
public func deleteAccount() async throws {
40+
try await apiClient.deleteAccount()
41+
}
2842
}

SNUTT/Modules/Feature/Auth/Sources/Infra/AuthUserState.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ public final class AuthUserState: AuthState {
4242
Task { @MainActor in
4343
isAuthenticatedSubject.send(true)
4444
}
45+
case .fcmToken:
46+
userDefaults[\.fcmToken] = value
4547
}
4648
}
4749

@@ -51,6 +53,8 @@ public final class AuthUserState: AuthState {
5153
store.withLock { $0[.userID] } ?? userDefaults[\.userID]
5254
case .accessToken:
5355
store.withLock { $0[.accessToken] }
56+
case .fcmToken:
57+
store.withLock { $0[.fcmToken] }
5458
}
5559
}
5660

@@ -68,4 +72,8 @@ extension UserDefaultsEntryDefinitions {
6872
var userID: UserDefaultsEntry<String?> {
6973
.init(key: "userID", defaultValue: nil)
7074
}
75+
76+
var fcmToken: UserDefaultsEntry<String?> {
77+
.init(key: "fcmToken", defaultValue: nil)
78+
}
7179
}

SNUTT/Modules/Feature/AuthInterface/Sources/AuthRepository.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import Spyable
1212

1313
@Spyable
1414
public protocol AuthRepository: Sendable {
15+
func addDevice(fcmToken: String) async throws
1516
func registerWithLocalID(localID: String, localPassword: String, email: String) async throws -> LoginResponse
1617
func loginWithLocalID(localID: String, localPassword: String) async throws -> LoginResponse
1718
// func loginWithApple(appleToken: String) async throws -> LoginResponseDto
@@ -21,7 +22,8 @@ public protocol AuthRepository: Sendable {
2122
// func sendVerificationCode(email: String) async throws
2223
// func checkVerificationCode(localId: String, code: String) async throws
2324
// func resetPassword(localId: String, password: String) async throws
24-
// func logout(userId: String, fcmToken: String) async throws -> LogoutResponseDto
25+
func logout(fcmToken: String) async throws
26+
func deleteAccount() async throws
2527
}
2628

2729
@MemberwiseInit(.public)

SNUTT/Modules/Feature/AuthInterface/Sources/AuthState.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public protocol AuthState: Sendable, ObservableObject, AnyObject {
2020
}
2121

2222
public enum AuthStateType: String, Sendable {
23-
case userID, accessToken
23+
case userID, accessToken, fcmToken
2424
}
2525

2626
public enum AuthStateKey: TestDependencyKey {
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//
2+
// AuthUseCaseProtocol.swift
3+
// SNUTT
4+
//
5+
// Copyright © 2025 wafflestudio.com. All rights reserved.
6+
//
7+
8+
import Dependencies
9+
import Spyable
10+
11+
@Spyable
12+
public protocol AuthUseCaseProtocol: Sendable {
13+
func syncAuthState()
14+
func loginWithLocalID(localID: String, localPassword: String) async throws
15+
func logout() async throws
16+
func registerFCMToken(_ token: String) async throws
17+
func deleteAccount() async throws
18+
}
19+
20+
public enum AuthUseCaseKey: TestDependencyKey {
21+
public static let testValue: any AuthUseCaseProtocol = AuthUseCaseProtocolSpy()
22+
public static let previewValue: any AuthUseCaseProtocol = AuthUseCaseProtocolSpy()
23+
}
24+
25+
extension DependencyValues {
26+
public var authUseCase: any AuthUseCaseProtocol {
27+
get { self[AuthUseCaseKey.self] }
28+
set { self[AuthUseCaseKey.self] = newValue }
29+
}
30+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"info" : {
3+
"author" : "xcode",
4+
"version" : 1
5+
}
6+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{
2+
"images" : [
3+
{
4+
"idiom" : "universal",
5+
"scale" : "1x"
6+
},
7+
{
8+
"appearances" : [
9+
{
10+
"appearance" : "luminosity",
11+
"value" : "dark"
12+
}
13+
],
14+
"idiom" : "universal",
15+
"scale" : "1x"
16+
},
17+
{
18+
"filename" : "account.person@2x.png",
19+
"idiom" : "universal",
20+
"scale" : "2x"
21+
},
22+
{
23+
"appearances" : [
24+
{
25+
"appearance" : "luminosity",
26+
"value" : "dark"
27+
}
28+
],
29+
"filename" : "account.person.dark@2x.png",
30+
"idiom" : "universal",
31+
"scale" : "2x"
32+
},
33+
{
34+
"filename" : "account.person@3x.png",
35+
"idiom" : "universal",
36+
"scale" : "3x"
37+
},
38+
{
39+
"appearances" : [
40+
{
41+
"appearance" : "luminosity",
42+
"value" : "dark"
43+
}
44+
],
45+
"filename" : "account.person.dark@3x.png",
46+
"idiom" : "universal",
47+
"scale" : "3x"
48+
}
49+
],
50+
"info" : {
51+
"author" : "xcode",
52+
"version" : 1
53+
}
54+
}

0 commit comments

Comments
 (0)