Skip to content

Commit afbc3dd

Browse files
authored
[#32] AccountView를 Store 프로토콜을 채택한 뷰모델을 사용하여 재구성한다 (#67)
* feat: 뷰모델 베이스 구현 * feat: 데이터 레이어 구현 * feat: 도메인 레이어 구현 * feat: 프레젠테이션, UI 레이어 구현 * chore: node.js 업데이트 * feat: 스크롤 방지
1 parent 1d6f305 commit afbc3dd

17 files changed

Lines changed: 499 additions & 100 deletions

DevLog/App/Assembler/DataAssembler.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,24 @@ final class DataAssembler: Assembler {
3737
AuthSessionRepositoryImpl(authService: container.resolve(AuthService.self))
3838
}
3939

40+
container.register(AuthDataRepository.self) {
41+
AuthDataRepositoryImpl(
42+
authService: container.resolve(AuthService.self),
43+
appleAuthService: container.resolve(
44+
AuthenticationService.self,
45+
name: "AppleAuthenticationService"
46+
),
47+
githubAuthService: container.resolve(
48+
AuthenticationService.self,
49+
name: "GithubAuthenticationService"
50+
),
51+
googleAuthService: container.resolve(
52+
AuthenticationService.self,
53+
name: "GoogleAuthenticationService"
54+
)
55+
)
56+
}
57+
4058
container.register(UserDataRepository.self) {
4159
UserDataRepositoryImpl(userService: container.resolve(UserService.self))
4260
}

DevLog/App/Assembler/DomainAssembler.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,17 @@ final class DomainAssembler: Assembler {
7070
container.register(FetchPushNotificationsUseCase.self) {
7171
FetchPushNotificationsUseCaseImpl(container.resolve(PushNotificationRepository.self))
7272
}
73+
74+
container.register(FetchAuthProvidersUseCase.self) {
75+
FetchAuthProvidersUseCaseImpl(container.resolve(AuthDataRepository.self))
76+
}
77+
78+
container.register(LinkAuthProviderUseCase.self) {
79+
LinkAuthProviderUseCaseImpl(container.resolve(AuthDataRepository.self))
80+
}
81+
82+
container.register(UnlinkAuthProviderUseCase.self) {
83+
UnlinkAuthProviderUseCaseImpl(container.resolve(AuthDataRepository.self))
84+
}
7385
}
7486
}

DevLog/Data/Common/Error+.swift

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

1010
enum AuthError: Error {
1111
case notAuthenticated
12+
case failedToUnlinkLastProvider
13+
case unsupportedProvider
1214
}
1315

1416
enum FirestoreError: Error, LocalizedError {
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//
2+
// AuthDataRepositoryImpl.swift
3+
// DevLog
4+
//
5+
// Created by 최윤진 on 2/12/26.
6+
//
7+
8+
import FirebaseAuth
9+
10+
final class AuthDataRepositoryImpl: AuthDataRepository {
11+
private let authService: AuthService
12+
private let appleAuthService: AuthenticationService
13+
private let githubAuthService: AuthenticationService
14+
private let googleAuthService: AuthenticationService
15+
16+
init(
17+
authService: AuthService,
18+
appleAuthService: AuthenticationService,
19+
githubAuthService: AuthenticationService,
20+
googleAuthService: AuthenticationService
21+
) {
22+
self.authService = authService
23+
self.appleAuthService = appleAuthService
24+
self.githubAuthService = githubAuthService
25+
self.googleAuthService = googleAuthService
26+
}
27+
28+
func fetchCurrentProvider() async throws -> AuthProvider? {
29+
guard let providerString = try await authService.getProviderID() else {
30+
return nil
31+
}
32+
return AuthProvider(rawValue: providerString)
33+
}
34+
35+
func fetchAllProviders() async throws -> [AuthProvider] {
36+
let providerStrings = authService.providerIDs ?? []
37+
return providerStrings.compactMap { AuthProvider(rawValue: $0) }
38+
}
39+
40+
func linkProvider(_ provider: AuthProvider) async throws {
41+
guard let uid = authService.uid,
42+
let user = Auth.auth().currentUser,
43+
let email = user.email else {
44+
throw AuthError.notAuthenticated
45+
}
46+
47+
let service: AuthenticationService
48+
switch provider {
49+
case .apple:
50+
service = appleAuthService
51+
case .google:
52+
service = googleAuthService
53+
case .github:
54+
service = githubAuthService
55+
}
56+
57+
try await service.link(uid: uid, email: email)
58+
}
59+
60+
func unlinkProvider(_ provider: AuthProvider) async throws {
61+
guard let uid = authService.uid,
62+
let user = Auth.auth().currentUser else {
63+
throw AuthError.notAuthenticated
64+
}
65+
66+
if user.providerData.count <= 1 {
67+
throw AuthError.failedToUnlinkLastProvider
68+
}
69+
70+
let service: AuthenticationService
71+
switch provider {
72+
case .apple:
73+
service = appleAuthService
74+
case .google:
75+
service = googleAuthService
76+
case .github:
77+
service = githubAuthService
78+
}
79+
80+
try await service.unlink(uid)
81+
}
82+
}

DevLog/Domain/Protocol/AuthDataRepository.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,16 @@
55
// Created by 최윤진 on 1/5/26.
66
//
77

8-
import Foundation
8+
protocol AuthDataRepository {
9+
/// 현재 로그인한 프로바이더를 가져옵니다
10+
func fetchCurrentProvider() async throws -> AuthProvider?
11+
12+
/// 연결된 모든 프로바이더 목록을 가져옵니다
13+
func fetchAllProviders() async throws -> [AuthProvider]
14+
15+
/// 특정 프로바이더를 계정에 연결합니다
16+
func linkProvider(_ provider: AuthProvider) async throws
17+
18+
/// 특정 프로바이더를 계정에서 해제합니다
19+
func unlinkProvider(_ provider: AuthProvider) async throws
20+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//
2+
// FetchAuthProvidersUseCase.swift
3+
// DevLog
4+
//
5+
// Created by 최윤진 on 2/12/26.
6+
//
7+
8+
protocol FetchAuthProvidersUseCase {
9+
func execute() async throws -> (currentProvider: AuthProvider?, allProviders: [AuthProvider])
10+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//
2+
// FetchAuthProvidersUseCaseImpl.swift
3+
// DevLog
4+
//
5+
// Created by 최윤진 on 2/12/26.
6+
//
7+
8+
final class FetchAuthProvidersUseCaseImpl: FetchAuthProvidersUseCase {
9+
private let repository: AuthDataRepository
10+
11+
init(_ repository: AuthDataRepository) {
12+
self.repository = repository
13+
}
14+
15+
func execute() async throws -> (currentProvider: AuthProvider?, allProviders: [AuthProvider]) {
16+
async let currentProvider = try await repository.fetchCurrentProvider()
17+
async let allProviders = try await repository.fetchAllProviders()
18+
19+
return try await (currentProvider, allProviders)
20+
}
21+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//
2+
// LinkAuthProviderUseCase.swift
3+
// DevLog
4+
//
5+
// Created by 최윤진 on 2/12/26.
6+
//
7+
8+
protocol LinkAuthProviderUseCase {
9+
func execute(_ provider: AuthProvider) async throws
10+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//
2+
// LinkAuthProviderUseCaseImpl.swift
3+
// DevLog
4+
//
5+
// Created by 최윤진 on 2/12/26.
6+
//
7+
8+
final class LinkAuthProviderUseCaseImpl: LinkAuthProviderUseCase {
9+
private let repository: AuthDataRepository
10+
11+
init(_ repository: AuthDataRepository) {
12+
self.repository = repository
13+
}
14+
15+
func execute(_ provider: AuthProvider) async throws {
16+
try await repository.linkProvider(provider)
17+
}
18+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//
2+
// UnlinkAuthProviderUseCase.swift
3+
// DevLog
4+
//
5+
// Created by 최윤진 on 2/12/26.
6+
//
7+
8+
protocol UnlinkAuthProviderUseCase {
9+
func execute(_ provider: AuthProvider) async throws
10+
}

0 commit comments

Comments
 (0)