From 1f3b9f45bd29a35b9e5616c082883195abd85164 Mon Sep 17 00:00:00 2001 From: kim-seonwoo Date: Wed, 9 Oct 2024 14:40:31 +0900 Subject: [PATCH 01/13] [Feat/#91] LoginUseCase --- .../Domain/LoginUseCase.swift | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Refactor_LoginFeature_Combine/Domain/LoginUseCase.swift diff --git a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Refactor_LoginFeature_Combine/Domain/LoginUseCase.swift b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Refactor_LoginFeature_Combine/Domain/LoginUseCase.swift new file mode 100644 index 00000000..f47800ff --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Refactor_LoginFeature_Combine/Domain/LoginUseCase.swift @@ -0,0 +1,64 @@ +// +// LoginUseCase.swift +// LoginFeatureInterface +// +// Created by Seonwoo Kim on 10/9/24. +// Copyright ยฉ 2024 HMH-iOS. All rights reserved. +// + +import Foundation +import Combine +import Core +import Domain + +public enum LoginResponseType { + case loginSuccess + case loginFailure + case noUserInfo +} + +protocol LoginUseCaseType { + func requestLogin(platform: String, socialToken: String) -> AnyPublisher + var loginResponse: CurrentValueSubject { get set } +} + +final class LoginUseCase: LoginUseCaseType { + + public var loginResponse = CurrentValueSubject(.loginFailure) + + + private var container: DIContainer + private var cancelBag = CancelBag() + + init(container: DIContainer) { + self.container = container + } + + func requestLogin(platform: String, socialToken: String) -> AnyPublisher { + return container.services.userService.signIn(platform: platform) + .handleEvents(receiveOutput: { response in + UserManager.shared.socialToken = socialToken + UserManager.shared.accessToken = response.data + UserManager.shared.refreshToken = response.data + }) + .map { response in + handleLoginResponse(statusCode: response.statusCode) + } + .eraseToAnyPublisher() + } + + private func handleLoginResponse(statusCode: Int) -> LoginResponseType { + switch statusCode { + case 200..<300: + self.loginResponse.send(.loginSuccess) + return .loginSuccess + case 403: + self.loginResponse.send(.noUserInfo) + return .noUserInfo + default: + self.loginResponse.send(.loginFailure) + return .loginFailure + } + } +} + From f1df98e25334c59a09f7e609a969610b57358a5c Mon Sep 17 00:00:00 2001 From: kim-seonwoo Date: Wed, 9 Oct 2024 15:49:03 +0900 Subject: [PATCH 02/13] [Feat/#91] LoginViewModel Refactor --- .../Feature/LoginViewModel_Refactor.swift | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Refactor_LoginFeature_Combine/Feature/LoginViewModel_Refactor.swift diff --git a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Refactor_LoginFeature_Combine/Feature/LoginViewModel_Refactor.swift b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Refactor_LoginFeature_Combine/Feature/LoginViewModel_Refactor.swift new file mode 100644 index 00000000..5385cf99 --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Refactor_LoginFeature_Combine/Feature/LoginViewModel_Refactor.swift @@ -0,0 +1,115 @@ +// +// LoginViewModel_refactore.swift +// LoginFeatureInterface +// +// Created by Seonwoo Kim on 10/9/24. +// Copyright ยฉ 2024 HMH-iOS. All rights reserved. +// + +import Combine +import Core +import DSKit +import AuthenticationServices +import KakaoSDKUser + +class LoginViewModel_Refactor: NSObject, ObservableObject { + + private var useCase: LoginUseCaseType + private var cancelBag = CancelBag() + + @Published private(set) var state = State( + loginStatus: .loginFailure + ) + + init(useCase: LoginUseCaseType) { + self.useCase = useCase + } + + enum Action { + case kakaoLoginDidTap + case appleLoginDidTap + } + + struct State { + var loginStatus: LoginResponseType + } + + func send(action: Action) { + switch action { + case .kakaoLoginDidTap: + handleKakaoLogin() + case .appleLoginDidTap: + handleAppleLogin() + } + } + + private func handleKakaoLogin() { + if (UserApi.isKakaoTalkLoginAvailable()) { + UserApi.shared.loginWithKakaoTalk { [weak self] (oauthToken, error) in + if let error = error { + print("Kakao login error: \(error)") + return + } + if let oauthToken = oauthToken { + let idToken = oauthToken.accessToken + let token = "Bearer " + idToken + self?.requestLoginWithSocialToken(platform: "KAKAO", token: token) + } + } + } else { + UserApi.shared.loginWithKakaoAccount { [weak self] (oauthToken, error) in + if let error = error { + print("Kakao account login error: \(error)") + return + } + if let oauthToken = oauthToken { + let idToken = oauthToken.accessToken + let token = "Bearer " + idToken + self?.requestLoginWithSocialToken(platform: "KAKAO", token: token) + } + } + } + } + + private func handleAppleLogin() { + let request = ASAuthorizationAppleIDProvider().createRequest() + request.requestedScopes = [.fullName, .email] + + let authorizationController = ASAuthorizationController(authorizationRequests: [request]) + authorizationController.delegate = self + authorizationController.performRequests() + } + + private func requestLoginWithSocialToken(platform: String, token: String) { + useCase.requestLogin(platform: platform, socialToken: token) + .sink { _ in + } receiveValue: {_ in + bindLoginResponse() + }.store(in: cancelBag) + } + + private func bindLoginResponse() { + useCase.loginResponse + .sink(receiveCompletion: { _ in }, receiveValue: { [weak self] response in + self?.state.loginStatus = response + }).store(in: cancelBag) + } +} + +extension LoginViewModel_Refactor: ASAuthorizationControllerDelegate { + + func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) { + if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential { + guard let identityToken = appleIDCredential.identityToken, + let idTokenString = String(data: identityToken, encoding: .utf8) else { + print("Failed to get Apple ID token") + return + } + requestLoginWithSocialToken(platform: "APPLE", token: idTokenString) + } + } + + func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) { + print("Apple login error: \(error.localizedDescription)") + } +} From 0c4c25beebeaabe79ce736c59e2d77bc130d7adb Mon Sep 17 00:00:00 2001 From: kim-seonwoo Date: Fri, 8 Nov 2024 14:20:21 +0900 Subject: [PATCH 03/13] =?UTF-8?q?[Feat/#91]=20LoginUseCase=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoginFeature/Sources/LoginUseCase.swift | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/LoginUseCase.swift diff --git a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/LoginUseCase.swift b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/LoginUseCase.swift new file mode 100644 index 00000000..ff690349 --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/LoginUseCase.swift @@ -0,0 +1,62 @@ +// +// LoginUseCase.swift +// LoginFeature +// +// Created by Seonwoo Kim on 11/8/24. +// Copyright ยฉ 2024 HMH-iOS. All rights reserved. +// + +import Foundation +import Combine + +import Domain +import Core + +public enum LoginResponseType { + case loginSuccess + case loginFailure + case onboardingNeeded +} + +public protocol LoginUseCaseType { + func login(provider: OAuthProviderType) -> AnyPublisher +} + +public final class LoginUseCase: LoginUseCaseType { + + private let repository: AuthRepositoryType + + public init(repository: AuthRepositoryType) { + self.repository = repository + } + + public func login(provider: Domain.OAuthProviderType) -> AnyPublisher { + repository.authorize(provider) + .handleEvents(receiveOutput: { socialToken in + UserManager.shared.socialToken = socialToken + }) + .flatMap { [weak self] _ -> AnyPublisher in + guard let self = self else { + return Fail(error: Domain.AuthError.appleAuthrizeError).eraseToAnyPublisher() + } + + return self.repository.socialLogin(socialPlatform: provider.rawValue) + .map { _ in LoginResponseType.loginSuccess } + .catch { error -> AnyPublisher in + switch error { + case .alreadyRegisteredUser: + return Just(.onboardingNeeded) + .setFailureType(to: Domain.AuthError.self) + .eraseToAnyPublisher() + default: + return Just(.loginFailure) + .setFailureType(to: Domain.AuthError.self) + .eraseToAnyPublisher() + } + } + .eraseToAnyPublisher() + } + .eraseToAnyPublisher() + } +} + From 89bf95558bbfaf9f7077d2586cade979f36692f6 Mon Sep 17 00:00:00 2001 From: kim-seonwoo Date: Fri, 8 Nov 2024 14:20:52 +0900 Subject: [PATCH 04/13] =?UTF-8?q?[Fix/#91]=20OAuthProviderType=20=EC=9B=90?= =?UTF-8?q?=EC=8B=9C=EA=B0=92=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Domain/Sources/Entity/Auth/OAuthProviderType.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/HMH_Tuist_iOS/Projects/Domain/Sources/Entity/Auth/OAuthProviderType.swift b/HMH_Tuist_iOS/Projects/Domain/Sources/Entity/Auth/OAuthProviderType.swift index 243724c5..449b6e43 100644 --- a/HMH_Tuist_iOS/Projects/Domain/Sources/Entity/Auth/OAuthProviderType.swift +++ b/HMH_Tuist_iOS/Projects/Domain/Sources/Entity/Auth/OAuthProviderType.swift @@ -8,7 +8,7 @@ import Foundation -public enum OAuthProviderType { - case kakao - case apple +public enum OAuthProviderType: String { + case kakao = "KAKAO" + case apple = "APPLE" } From aa1c87240bddce5576155c19f7d77098f8b26bf1 Mon Sep 17 00:00:00 2001 From: kim-seonwoo Date: Fri, 8 Nov 2024 15:19:30 +0900 Subject: [PATCH 05/13] =?UTF-8?q?[Refactor/#91]=20LoginViewModel=20?= =?UTF-8?q?=EB=A6=AC=ED=8E=99=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/ViewModels/LoginViewModel.swift | 114 +++--------------- 1 file changed, 20 insertions(+), 94 deletions(-) diff --git a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/ViewModels/LoginViewModel.swift b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/ViewModels/LoginViewModel.swift index 69304827..a2daa9f1 100644 --- a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/ViewModels/LoginViewModel.swift +++ b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/ViewModels/LoginViewModel.swift @@ -1,106 +1,32 @@ -import SwiftUI -import AuthenticationServices +// +// LoginViewModel.swift +// LoginFeature +// +// Created by Seonwoo Kim on 11/8/24. +// Copyright ยฉ 2024 HMH-iOS. All rights reserved. +// -import KakaoSDKUser +import Foundation import Core -import DSKit +import Domain -public class LoginViewModel: NSObject, ObservableObject { +final class LoginViewModel: ObservableObject { + @Published var loginStatus: LoginResponseType = .loginFailure - @Published public var isLoading: Bool = true - @Published var isPresented: Bool = false - @Published var alertType: CustomAlertType = .unlock + private var cancelBag = CancelBag() - public func handleSplashScreen() { - self.isLoading = false - } + private let loginUseCase: LoginUseCaseType - func handleAppleLogin() { - let request = ASAuthorizationAppleIDProvider().createRequest() - request.requestedScopes = [.fullName, .email] - - let authorizationController = ASAuthorizationController(authorizationRequests: [request]) - authorizationController.delegate = self - authorizationController.performRequests() + init(loginUseCase: LoginUseCaseType) { + self.loginUseCase = loginUseCase } - func handleKakaoLogin() { - if (UserApi.isKakaoTalkLoginAvailable()) { - UserApi.shared.loginWithKakaoTalk {(oauthToken, error) in - if let error = error { - print(error) - } - if let oauthToken = oauthToken{ - let idToken = oauthToken.accessToken - UserManager.shared.socialPlatform = "KAKAO" - UserManager.shared.socialToken = "Bearer " + idToken - self.postSocialLoginData() - } - } - } else { - UserApi.shared.loginWithKakaoAccount {(oauthToken, error) in - if let error = error { - print("๐Ÿ€",error) - } - if let oauthToken = oauthToken{ - print("kakao success") - UserManager.shared.socialPlatform = "KAKAO" - let idToken = oauthToken.accessToken - UserManager.shared.socialToken = "Bearer " + idToken - self.postSocialLoginData() - } + func handleLoginButton(provider: OAuthProviderType) { + loginUseCase.login(provider: provider) + .sink(receiveCompletion: { _ in }) { [weak self] response in + self?.loginStatus = response } - } - } - - //TODO: ๋„คํŠธ์›Œํฌ ๋ถ€๋ถ„์€ ์˜์กด์„ฑ ์ •๋ฆฌํ•œ ๋’ค์— ๋‹ค์‹œ ์—ฐ๊ฒฐํ•ด๋ด…์‹œ๋‹ค - func postSocialLoginData() { -// let provider = Providers.AuthProvider -// let request = SocialLoginRequestDTO(socialPlatform: UserManager.shared.socialPlatform ?? "") -// -// provider.request(target: .socialLogin(data: request), instance: BaseResponse.self) { data in -// if data.status == 403 { -// UserManager.shared.appStateString = "onboarding" -// } else if data.status == 200 { -// guard let data = data.data else { return } -// UserManager.shared.refreshToken = data.token.refreshToken -// UserManager.shared.accessToken = data.token.accessToken -// UserManager.shared.appStateString = "home" -// } -// } - } -} - -extension LoginViewModel: ASAuthorizationControllerDelegate { - - public func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) { - switch authorization.credential { - case let appleIDCredential as ASAuthorizationAppleIDCredential: - let userIdentifier = appleIDCredential.user - let fullName = appleIDCredential.fullName - - if let identityToken = appleIDCredential.identityToken, - let identifyTokenString = String(data: identityToken, encoding: .utf8) { - UserManager.shared.socialToken = identifyTokenString - UserManager.shared.socialPlatform = "APPLE" - self.postSocialLoginData() - } else { - print("Identity token is nil or failed to convert to string.") - } - default: - break - } - } - - func handleAppleIDCredential(_ credential: ASAuthorizationAppleIDCredential) { - let fullName = credential.fullName - let name = (fullName?.familyName ?? "") + (fullName?.givenName ?? "") - UserManager.shared.userName = name - guard let idToken = String(data: credential.identityToken ?? Data(), encoding: .utf8) else { return print("no idToken!!") } - } - - public func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) { - print(error.localizedDescription) + .store(in: cancelBag) } } From 641be9e6f7de6259485a2f2a22a9e993d016c897 Mon Sep 17 00:00:00 2001 From: kim-seonwoo Date: Fri, 8 Nov 2024 15:25:53 +0900 Subject: [PATCH 06/13] =?UTF-8?q?[Refactor/#91]=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EB=B7=B0=20=EA=B4=80=EB=A0=A8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoginFeature/Sources/LoginView.swift | 14 +++++----- .../Sources/Views/LoginButton.swift | 27 ++++--------------- .../Sources/Views/SwipeView.swift | 10 +++---- 3 files changed, 16 insertions(+), 35 deletions(-) diff --git a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/LoginView.swift b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/LoginView.swift index ea6245b4..a9f393ca 100644 --- a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/LoginView.swift +++ b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/LoginView.swift @@ -6,14 +6,14 @@ // import SwiftUI -import AuthenticationServices import DSKit +import Domain public struct LoginView: View { @ObservedObject var viewModel: LoginViewModel - public init(viewModel: LoginViewModel) { + init(viewModel: LoginViewModel) { self.viewModel = viewModel } @@ -23,15 +23,13 @@ public struct LoginView: View { .ignoresSafeArea() VStack(spacing: 10) { //TODO: ์ด๋ฏธ์ง€ ํƒ€์ž… ๋ฌธ์ œ๊ฑฐ ๊ฐ™์€๋ฐ ์ง€๊ธˆ ํ•ด๊ฒฐํ•˜๊ธฐ์—” ์‹ฑ์‹ฑ๋ฏธ์—ญ -// SwipeView(imageNames: [.onboardingFirst, .onboardingSecond, .onboardingThird]) -// .padding(.bottom, 75) - LoginButton(loginProvider: .kakao, viewModel: viewModel) - LoginButton(loginProvider: .apple, viewModel: viewModel) + SwipeView(swipeImages: [DSKitAsset.onboardingFirst.swiftUIImage, DSKitAsset.onboardingSecond.swiftUIImage, DSKitAsset.onboardingThird.swiftUIImage]) + .padding(.bottom, 75) + LoginButton(loginProvider: OAuthProviderType.kakao, viewModel: viewModel) + LoginButton(loginProvider: OAuthProviderType.apple, viewModel: viewModel) } } .frame(maxHeight: .infinity) .padding(.vertical, 22) } } - - diff --git a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Views/LoginButton.swift b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Views/LoginButton.swift index 1765749d..ed2abae7 100644 --- a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Views/LoginButton.swift +++ b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Views/LoginButton.swift @@ -6,42 +6,25 @@ // import SwiftUI -import AuthenticationServices import DSKit - -enum SignInProvider { - case apple - case kakao - - var signInLogoImage: String { - switch self { - case .apple: - return "appleLogo" - case .kakao: - return "kakaoLogo" - } - } -} +import Domain struct LoginButton: View { - let loginProvider: SignInProvider + var loginProvider: OAuthProviderType = .apple @ObservedObject var viewModel: LoginViewModel + var signInLogoImage = DSKitAsset.appleLogo.swiftUIImage var body: some View { Button(action: { - if loginProvider == .apple { - viewModel.handleAppleLogin() - } else if loginProvider == .kakao { - viewModel.handleKakaoLogin() - } + viewModel.handleLoginButton(provider: loginProvider) }) { RoundedRectangle(cornerRadius: 6.3) .frame(width:336, height: 51) .foregroundColor(loginProvider == .apple ? DSKitAsset.whiteBtn.swiftUIColor : DSKitAsset.yelloBtn.swiftUIColor) .overlay( HStack { - Image(loginProvider.signInLogoImage) + Image(uiImage: loginProvider == .apple ? DSKitAsset.appleLogo.image : DSKitAsset.kakaoLogo.image) .resizable() .frame(width: 24, height: 24) .padding(.leading, 14) diff --git a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Views/SwipeView.swift b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Views/SwipeView.swift index 8e4f56c6..38d7fa15 100644 --- a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Views/SwipeView.swift +++ b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Views/SwipeView.swift @@ -10,7 +10,7 @@ import SwiftUI import DSKit struct SwipeView: View { - var imageNames: [ImageResource] + var swipeImages: [Image] private let timer = Timer.publish(every: 3.0, on: .main, in: .common).autoconnect() @State private var selectedImageIndex: Int = 0 @@ -18,8 +18,8 @@ struct SwipeView: View { var body: some View { VStack { TabView(selection: $selectedImageIndex) { - ForEach(0.. Date: Mon, 11 Nov 2024 20:03:34 +0900 Subject: [PATCH 07/13] =?UTF-8?q?[Fix/#91]=20Domain=20=EC=B2=98=EB=A6=AC?= =?UTF-8?q?=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Features/LoginFeature/Sources/LoginUseCase.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/LoginUseCase.swift b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/LoginUseCase.swift index ff690349..1fb7b6a7 100644 --- a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/LoginUseCase.swift +++ b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/LoginUseCase.swift @@ -35,16 +35,16 @@ public final class LoginUseCase: LoginUseCaseType { .handleEvents(receiveOutput: { socialToken in UserManager.shared.socialToken = socialToken }) - .flatMap { [weak self] _ -> AnyPublisher in + .flatMap { [weak self] _ -> AnyPublisher in guard let self = self else { - return Fail(error: Domain.AuthError.appleAuthrizeError).eraseToAnyPublisher() + return Fail(error: AuthError.appleAuthrizeError).eraseToAnyPublisher() } return self.repository.socialLogin(socialPlatform: provider.rawValue) .map { _ in LoginResponseType.loginSuccess } - .catch { error -> AnyPublisher in + .catch { error -> AnyPublisher in switch error { - case .alreadyRegisteredUser: + case .unregisteredUser: return Just(.onboardingNeeded) .setFailureType(to: Domain.AuthError.self) .eraseToAnyPublisher() From 562e2b33968f8e78c77cd243ec03a2b98dcd1572 Mon Sep 17 00:00:00 2001 From: kim-seonwoo Date: Tue, 12 Nov 2024 22:57:27 +0900 Subject: [PATCH 08/13] =?UTF-8?q?[Fix/#91]=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=EB=B6=80=EB=B6=84=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Domain/LoginUseCase.swift | 64 ---------- .../Feature/LoginViewModel_Refactor.swift | 115 ------------------ 2 files changed, 179 deletions(-) delete mode 100644 HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Refactor_LoginFeature_Combine/Domain/LoginUseCase.swift delete mode 100644 HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Refactor_LoginFeature_Combine/Feature/LoginViewModel_Refactor.swift diff --git a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Refactor_LoginFeature_Combine/Domain/LoginUseCase.swift b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Refactor_LoginFeature_Combine/Domain/LoginUseCase.swift deleted file mode 100644 index f47800ff..00000000 --- a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Refactor_LoginFeature_Combine/Domain/LoginUseCase.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// LoginUseCase.swift -// LoginFeatureInterface -// -// Created by Seonwoo Kim on 10/9/24. -// Copyright ยฉ 2024 HMH-iOS. All rights reserved. -// - -import Foundation -import Combine -import Core -import Domain - -public enum LoginResponseType { - case loginSuccess - case loginFailure - case noUserInfo -} - -protocol LoginUseCaseType { - func requestLogin(platform: String, socialToken: String) -> AnyPublisher - var loginResponse: CurrentValueSubject { get set } -} - -final class LoginUseCase: LoginUseCaseType { - - public var loginResponse = CurrentValueSubject(.loginFailure) - - - private var container: DIContainer - private var cancelBag = CancelBag() - - init(container: DIContainer) { - self.container = container - } - - func requestLogin(platform: String, socialToken: String) -> AnyPublisher { - return container.services.userService.signIn(platform: platform) - .handleEvents(receiveOutput: { response in - UserManager.shared.socialToken = socialToken - UserManager.shared.accessToken = response.data - UserManager.shared.refreshToken = response.data - }) - .map { response in - handleLoginResponse(statusCode: response.statusCode) - } - .eraseToAnyPublisher() - } - - private func handleLoginResponse(statusCode: Int) -> LoginResponseType { - switch statusCode { - case 200..<300: - self.loginResponse.send(.loginSuccess) - return .loginSuccess - case 403: - self.loginResponse.send(.noUserInfo) - return .noUserInfo - default: - self.loginResponse.send(.loginFailure) - return .loginFailure - } - } -} - diff --git a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Refactor_LoginFeature_Combine/Feature/LoginViewModel_Refactor.swift b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Refactor_LoginFeature_Combine/Feature/LoginViewModel_Refactor.swift deleted file mode 100644 index 5385cf99..00000000 --- a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Refactor_LoginFeature_Combine/Feature/LoginViewModel_Refactor.swift +++ /dev/null @@ -1,115 +0,0 @@ -// -// LoginViewModel_refactore.swift -// LoginFeatureInterface -// -// Created by Seonwoo Kim on 10/9/24. -// Copyright ยฉ 2024 HMH-iOS. All rights reserved. -// - -import Combine -import Core -import DSKit -import AuthenticationServices -import KakaoSDKUser - -class LoginViewModel_Refactor: NSObject, ObservableObject { - - private var useCase: LoginUseCaseType - private var cancelBag = CancelBag() - - @Published private(set) var state = State( - loginStatus: .loginFailure - ) - - init(useCase: LoginUseCaseType) { - self.useCase = useCase - } - - enum Action { - case kakaoLoginDidTap - case appleLoginDidTap - } - - struct State { - var loginStatus: LoginResponseType - } - - func send(action: Action) { - switch action { - case .kakaoLoginDidTap: - handleKakaoLogin() - case .appleLoginDidTap: - handleAppleLogin() - } - } - - private func handleKakaoLogin() { - if (UserApi.isKakaoTalkLoginAvailable()) { - UserApi.shared.loginWithKakaoTalk { [weak self] (oauthToken, error) in - if let error = error { - print("Kakao login error: \(error)") - return - } - if let oauthToken = oauthToken { - let idToken = oauthToken.accessToken - let token = "Bearer " + idToken - self?.requestLoginWithSocialToken(platform: "KAKAO", token: token) - } - } - } else { - UserApi.shared.loginWithKakaoAccount { [weak self] (oauthToken, error) in - if let error = error { - print("Kakao account login error: \(error)") - return - } - if let oauthToken = oauthToken { - let idToken = oauthToken.accessToken - let token = "Bearer " + idToken - self?.requestLoginWithSocialToken(platform: "KAKAO", token: token) - } - } - } - } - - private func handleAppleLogin() { - let request = ASAuthorizationAppleIDProvider().createRequest() - request.requestedScopes = [.fullName, .email] - - let authorizationController = ASAuthorizationController(authorizationRequests: [request]) - authorizationController.delegate = self - authorizationController.performRequests() - } - - private func requestLoginWithSocialToken(platform: String, token: String) { - useCase.requestLogin(platform: platform, socialToken: token) - .sink { _ in - } receiveValue: {_ in - bindLoginResponse() - }.store(in: cancelBag) - } - - private func bindLoginResponse() { - useCase.loginResponse - .sink(receiveCompletion: { _ in }, receiveValue: { [weak self] response in - self?.state.loginStatus = response - }).store(in: cancelBag) - } -} - -extension LoginViewModel_Refactor: ASAuthorizationControllerDelegate { - - func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) { - if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential { - guard let identityToken = appleIDCredential.identityToken, - let idTokenString = String(data: identityToken, encoding: .utf8) else { - print("Failed to get Apple ID token") - return - } - requestLoginWithSocialToken(platform: "APPLE", token: idTokenString) - } - } - - func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) { - print("Apple login error: \(error.localizedDescription)") - } -} From 9da540916b8d48ee0aadac00ccf8c480323bab6e Mon Sep 17 00:00:00 2001 From: kim-seonwoo Date: Thu, 14 Nov 2024 09:02:06 +0900 Subject: [PATCH 09/13] =?UTF-8?q?[Feat/#91]=20=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=20=EA=B4=80=EB=A0=A8=20info=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../App/Derived/InfoPlists/HMH-iOS-Info.plist | 152 ++++++++++-------- 1 file changed, 82 insertions(+), 70 deletions(-) diff --git a/HMH_Tuist_iOS/Projects/App/Derived/InfoPlists/HMH-iOS-Info.plist b/HMH_Tuist_iOS/Projects/App/Derived/InfoPlists/HMH-iOS-Info.plist index a3d5de43..a58e1a02 100644 --- a/HMH_Tuist_iOS/Projects/App/Derived/InfoPlists/HMH-iOS-Info.plist +++ b/HMH_Tuist_iOS/Projects/App/Derived/InfoPlists/HMH-iOS-Info.plist @@ -2,75 +2,87 @@ - BASE_URL - $(BASE_URL) - BGTaskSchedulerPermittedIdentifiers - - com.HMH.dailyTask - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleURLTypes - - - CFBundleTypeRole - Editor - CFBundleURLSchemes - - kakao$(KAKAO_API_KEY) - - - - CFBundleVersion - 1 - KAKAO_API_KEY - $(KAKAO_API_KEY) - LSApplicationQueriesSchemes - - kakaokompassauth - kakaolink - - LSRequiresIPhoneOS - - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - - UIAppFonts - - Pretendard-Regular.otf - Pretendard-SemiBold.otf - Pretendard-Medium.otf - - UIApplicationSceneManifest - - UIApplicationSupportsMultipleScenes - - UISceneConfigurations - - - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - + BASE_URL + $(BASE_URL) + BGTaskSchedulerPermittedIdentifiers + + com.HMH.dailyTask + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLSchemes + + kakao$(KAKAO_API_KEY) + + + + CFBundleVersion + 1 + KAKAO_API_KEY + $(KAKAO_API_KEY) + LSApplicationQueriesSchemes + + kakaokompassauth + kakaolink + + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + UIAppFonts + + Pretendard-Regular.otf + Pretendard-SemiBold.otf + Pretendard-Medium.otf + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + + + + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + From e9f28921c0826296ad232c235a4020ab993dcdb9 Mon Sep 17 00:00:00 2001 From: kim-seonwoo Date: Thu, 14 Nov 2024 10:06:54 +0900 Subject: [PATCH 10/13] =?UTF-8?q?[Fix/#91]=20Action=20State=20=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/ViewModels/LoginViewModel.swift | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/ViewModels/LoginViewModel.swift b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/ViewModels/LoginViewModel.swift index a2daa9f1..ecc13e7d 100644 --- a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/ViewModels/LoginViewModel.swift +++ b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/ViewModels/LoginViewModel.swift @@ -7,26 +7,43 @@ // import Foundation +import Combine import Core import Domain -final class LoginViewModel: ObservableObject { - @Published var loginStatus: LoginResponseType = .loginFailure +public final class LoginViewModel: ObservableObject { + private let loginUseCase: LoginUseCaseType private var cancelBag = CancelBag() - private let loginUseCase: LoginUseCaseType + // ํ™”๋ฉด ์ด๋™ ๋กœ์ง์— ์ ์šฉ ํ•„์š” + @Published private(set) var state = State(loginStatus: .loginFailure) - init(loginUseCase: LoginUseCaseType) { + public init(loginUseCase: LoginUseCaseType) { self.loginUseCase = loginUseCase } - func handleLoginButton(provider: OAuthProviderType) { - loginUseCase.login(provider: provider) - .sink(receiveCompletion: { _ in }) { [weak self] response in - self?.loginStatus = response - } - .store(in: cancelBag) + // MARK: Action + + enum Action { + case loginButtonDidTap(provider: OAuthProviderType) + } + + // MARK: State + + struct State { + var loginStatus: LoginResponseType + } + + func send(action: Action) { + switch action { + case .loginButtonDidTap(let provider): + loginUseCase.login(provider: provider) + .sink(receiveCompletion: { _ in }) { [weak self] response in + self?.state.loginStatus = response + } + .store(in: cancelBag) + } } } From 2d261fa17429707b14e68b93c3bc34539ceb36e3 Mon Sep 17 00:00:00 2001 From: kim-seonwoo Date: Thu, 14 Nov 2024 13:12:36 +0900 Subject: [PATCH 11/13] =?UTF-8?q?[Feat/#91]=20Swipe=20View=20ViewModel?= =?UTF-8?q?=EB=A1=9C=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Common/Custom/SplashView.swift | 2 +- .../LoginFeature/Sources/LoginView.swift | 4 ++-- .../Sources/ViewModels/LoginViewModel.swift | 22 ++++++++++++++++-- .../Sources/Views/LoginButton.swift | 2 +- .../Sources/Views/SwipeView.swift | 23 ++++++++----------- 5 files changed, 34 insertions(+), 19 deletions(-) diff --git a/HMH_Tuist_iOS/Projects/App/Sources/Common/Custom/SplashView.swift b/HMH_Tuist_iOS/Projects/App/Sources/Common/Custom/SplashView.swift index 8acf9880..f2ae4aaa 100644 --- a/HMH_Tuist_iOS/Projects/App/Sources/Common/Custom/SplashView.swift +++ b/HMH_Tuist_iOS/Projects/App/Sources/Common/Custom/SplashView.swift @@ -30,7 +30,7 @@ struct SplashView: View { .background(DSKitAsset.blackground.swiftUIColor, ignoresSafeAreaEdges: .all) .onAppear { DispatchQueue.main.asyncAfter(deadline: .now() + 3.5, execute: { - viewModel.handleSplashScreen() +// viewModel.handleSplashScreen() }) } } diff --git a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/LoginView.swift b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/LoginView.swift index a9f393ca..00e7f9b2 100644 --- a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/LoginView.swift +++ b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/LoginView.swift @@ -13,7 +13,7 @@ import Domain public struct LoginView: View { @ObservedObject var viewModel: LoginViewModel - init(viewModel: LoginViewModel) { + public init(viewModel: LoginViewModel) { self.viewModel = viewModel } @@ -23,7 +23,7 @@ public struct LoginView: View { .ignoresSafeArea() VStack(spacing: 10) { //TODO: ์ด๋ฏธ์ง€ ํƒ€์ž… ๋ฌธ์ œ๊ฑฐ ๊ฐ™์€๋ฐ ์ง€๊ธˆ ํ•ด๊ฒฐํ•˜๊ธฐ์—” ์‹ฑ์‹ฑ๋ฏธ์—ญ - SwipeView(swipeImages: [DSKitAsset.onboardingFirst.swiftUIImage, DSKitAsset.onboardingSecond.swiftUIImage, DSKitAsset.onboardingThird.swiftUIImage]) + SwipeView(swipeImages: [DSKitAsset.onboardingFirst.swiftUIImage, DSKitAsset.onboardingSecond.swiftUIImage, DSKitAsset.onboardingThird.swiftUIImage], viewModel: viewModel) .padding(.bottom, 75) LoginButton(loginProvider: OAuthProviderType.kakao, viewModel: viewModel) LoginButton(loginProvider: OAuthProviderType.apple, viewModel: viewModel) diff --git a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/ViewModels/LoginViewModel.swift b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/ViewModels/LoginViewModel.swift index ecc13e7d..3b5fb6c9 100644 --- a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/ViewModels/LoginViewModel.swift +++ b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/ViewModels/LoginViewModel.swift @@ -11,29 +11,43 @@ import Combine import Core import Domain +import DSKit public final class LoginViewModel: ObservableObject { private let loginUseCase: LoginUseCaseType private var cancelBag = CancelBag() - // ํ™”๋ฉด ์ด๋™ ๋กœ์ง์— ์ ์šฉ ํ•„์š” - @Published private(set) var state = State(loginStatus: .loginFailure) + // ํ™”๋ฉด ์ด๋™ ๋กœ์ง๊ณผ ์Šค์™€์ดํ”„ ์ธ๋ฑ์Šค ํฌํ•จ + @Published private(set) var state = State(loginStatus: .loginFailure, swipeImageIndex: 0) public init(loginUseCase: LoginUseCaseType) { self.loginUseCase = loginUseCase + startImageTimer() } // MARK: Action enum Action { case loginButtonDidTap(provider: OAuthProviderType) + case updateSwipeIndex + case setSwipeIndex(index: Int) } // MARK: State struct State { var loginStatus: LoginResponseType + var swipeImageIndex: Int + } + + private func startImageTimer() { + Timer.publish(every: 3.0, on: .main, in: .common) + .autoconnect() + .sink { [weak self] _ in + self?.send(action: .updateSwipeIndex) + } + .store(in: cancelBag) } func send(action: Action) { @@ -44,6 +58,10 @@ public final class LoginViewModel: ObservableObject { self?.state.loginStatus = response } .store(in: cancelBag) + case .updateSwipeIndex: + self.state.swipeImageIndex = (state.swipeImageIndex + 1) % 3 + case .setSwipeIndex(let index): + self.state.swipeImageIndex = index } } } diff --git a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Views/LoginButton.swift b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Views/LoginButton.swift index ed2abae7..c2d65a55 100644 --- a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Views/LoginButton.swift +++ b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Views/LoginButton.swift @@ -17,7 +17,7 @@ struct LoginButton: View { var body: some View { Button(action: { - viewModel.handleLoginButton(provider: loginProvider) + viewModel.send(action: .loginButtonDidTap(provider: loginProvider)) }) { RoundedRectangle(cornerRadius: 6.3) .frame(width:336, height: 51) diff --git a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Views/SwipeView.swift b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Views/SwipeView.swift index 38d7fa15..7a78d0c1 100644 --- a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Views/SwipeView.swift +++ b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Views/SwipeView.swift @@ -6,18 +6,20 @@ // import SwiftUI - import DSKit struct SwipeView: View { var swipeImages: [Image] - private let timer = Timer.publish(every: 3.0, on: .main, in: .common).autoconnect() - - @State private var selectedImageIndex: Int = 0 + @ObservedObject var viewModel: LoginViewModel var body: some View { VStack { - TabView(selection: $selectedImageIndex) { + TabView(selection: Binding( + get: { viewModel.state.swipeImageIndex }, + set: { index in + viewModel.send(action: .setSwipeIndex(index: index)) + } + )) { ForEach(0.. Date: Thu, 14 Nov 2024 13:16:16 +0900 Subject: [PATCH 12/13] =?UTF-8?q?[Fix/#91]=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20Domain=20=ED=82=A4=EC=9B=8C=EB=93=9C=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Features/LoginFeature/Sources/LoginUseCase.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/LoginUseCase.swift b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/LoginUseCase.swift index 1fb7b6a7..272c858e 100644 --- a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/LoginUseCase.swift +++ b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/LoginUseCase.swift @@ -30,7 +30,7 @@ public final class LoginUseCase: LoginUseCaseType { self.repository = repository } - public func login(provider: Domain.OAuthProviderType) -> AnyPublisher { + public func login(provider: OAuthProviderType) -> AnyPublisher { repository.authorize(provider) .handleEvents(receiveOutput: { socialToken in UserManager.shared.socialToken = socialToken @@ -46,11 +46,11 @@ public final class LoginUseCase: LoginUseCaseType { switch error { case .unregisteredUser: return Just(.onboardingNeeded) - .setFailureType(to: Domain.AuthError.self) + .setFailureType(to: AuthError.self) .eraseToAnyPublisher() default: return Just(.loginFailure) - .setFailureType(to: Domain.AuthError.self) + .setFailureType(to: AuthError.self) .eraseToAnyPublisher() } } From a4400aaa773e9a4b02162b65eb0ba8e658a5a5d5 Mon Sep 17 00:00:00 2001 From: kim-seonwoo Date: Thu, 14 Nov 2024 13:31:06 +0900 Subject: [PATCH 13/13] =?UTF-8?q?[Fix/#91]=20ViewModel=20Action=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/ViewModels/LoginViewModel.swift | 26 +++++++++---------- .../Sources/Views/SwipeView.swift | 4 +-- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/ViewModels/LoginViewModel.swift b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/ViewModels/LoginViewModel.swift index 3b5fb6c9..43f44a40 100644 --- a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/ViewModels/LoginViewModel.swift +++ b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/ViewModels/LoginViewModel.swift @@ -30,8 +30,7 @@ public final class LoginViewModel: ObservableObject { enum Action { case loginButtonDidTap(provider: OAuthProviderType) - case updateSwipeIndex - case setSwipeIndex(index: Int) + case swipeButtonDidTap(index: Int) } // MARK: State @@ -41,15 +40,6 @@ public final class LoginViewModel: ObservableObject { var swipeImageIndex: Int } - private func startImageTimer() { - Timer.publish(every: 3.0, on: .main, in: .common) - .autoconnect() - .sink { [weak self] _ in - self?.send(action: .updateSwipeIndex) - } - .store(in: cancelBag) - } - func send(action: Action) { switch action { case .loginButtonDidTap(let provider): @@ -58,10 +48,18 @@ public final class LoginViewModel: ObservableObject { self?.state.loginStatus = response } .store(in: cancelBag) - case .updateSwipeIndex: - self.state.swipeImageIndex = (state.swipeImageIndex + 1) % 3 - case .setSwipeIndex(let index): + case .swipeButtonDidTap(let index): self.state.swipeImageIndex = index } } + + private func startImageTimer() { + Timer.publish(every: 3.0, on: .main, in: .common) + .autoconnect() + .sink { [weak self] _ in + self?.state.swipeImageIndex = ((self?.state.swipeImageIndex ?? 0) + 1) % 3 + } + .store(in: cancelBag) + } + } diff --git a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Views/SwipeView.swift b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Views/SwipeView.swift index 7a78d0c1..918125a9 100644 --- a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Views/SwipeView.swift +++ b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Views/SwipeView.swift @@ -17,7 +17,7 @@ struct SwipeView: View { TabView(selection: Binding( get: { viewModel.state.swipeImageIndex }, set: { index in - viewModel.send(action: .setSwipeIndex(index: index)) + viewModel.send(action: .swipeButtonDidTap(index: index)) } )) { ForEach(0..