Skip to content

Commit

Permalink
🔀 :: (#10) 비밀번호 찾기 퍼블리싱
Browse files Browse the repository at this point in the history
  • Loading branch information
HongSJae authored Apr 29, 2024
2 parents 583857c + 42f064c commit 42bf49a
Show file tree
Hide file tree
Showing 27 changed files with 484 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ extension ModulePaths: MicroTargetPathConvertable {

public extension ModulePaths {
enum Feature: String, MicroTargetPathConvertable {
case FindPasswordFeature
case SignupFeature
case SigninFeature
case SplashFeature
Expand Down
13 changes: 13 additions & 0 deletions Projects/App/Sources/Application/DI/AppComponent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import SigninFeature
import SigninFeatureInterface
import SignupFeature
import SignupFeatureInterface
import FindPasswordFeature
import FindPasswordFeatureInterface

public final class AppComponent: BootstrapComponent {
// private let _keychain: any Keychain
Expand Down Expand Up @@ -63,4 +65,15 @@ public extension AppComponent {
var signupCheckLevelFactory: any SignupCheckLevelFactory {
SignupCheckLevelComponent(parent: self)
}

// Find Password
var inputEmailFactory: any InputEmailFactory {
InputEmailComponent(parent: self)
}
var verifyAuthCodeFactory: any VerifyAuthCodeFactory {
VerifyAuthCodeComponent(parent: self)
}
var inputNewPasswordFactory: any InputNewPasswordFactory {
InputNewPasswordComponent(parent: self)
}
}
64 changes: 64 additions & 0 deletions Projects/App/Sources/Application/NeedleGenerated.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@


import FindPasswordFeature
import FindPasswordFeatureInterface
import NeedleFoundation
import RootFeature
import RootFeatureInterface
Expand Down Expand Up @@ -162,6 +164,9 @@ private class SigninDependencyde06a9d0b22764487733Provider: SigninDependency {
var signupEmailFactory: any SignupEmailFactory {
return appComponent.signupEmailFactory
}
var inputEmailFactory: any InputEmailFactory {
return appComponent.inputEmailFactory
}
private let appComponent: AppComponent
init(appComponent: AppComponent) {
self.appComponent = appComponent
Expand All @@ -171,6 +176,43 @@ private class SigninDependencyde06a9d0b22764487733Provider: SigninDependency {
private func factory2882a056d84a613debccf47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject {
return SigninDependencyde06a9d0b22764487733Provider(appComponent: parent1(component) as! AppComponent)
}
private class VerifyAuthCodeDependencya16ab19f97e0892b555bProvider: VerifyAuthCodeDependency {
var inputNewPasswordFactory: any InputNewPasswordFactory {
return appComponent.inputNewPasswordFactory
}
private let appComponent: AppComponent
init(appComponent: AppComponent) {
self.appComponent = appComponent
}
}
/// ^->AppComponent->VerifyAuthCodeComponent
private func factoryed5ce75de1bf576b84adf47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject {
return VerifyAuthCodeDependencya16ab19f97e0892b555bProvider(appComponent: parent1(component) as! AppComponent)
}
private class InputNewPasswordDependency1149e32e41e1cfce6f6dProvider: InputNewPasswordDependency {


init() {

}
}
/// ^->AppComponent->InputNewPasswordComponent
private func factory27615059458a0576f404e3b0c44298fc1c149afb(_ component: NeedleFoundation.Scope) -> AnyObject {
return InputNewPasswordDependency1149e32e41e1cfce6f6dProvider()
}
private class InputEmailDependency4102766a436592066e97Provider: InputEmailDependency {
var verifyAuthCodeFactory: any VerifyAuthCodeFactory {
return appComponent.verifyAuthCodeFactory
}
private let appComponent: AppComponent
init(appComponent: AppComponent) {
self.appComponent = appComponent
}
}
/// ^->AppComponent->InputEmailComponent
private func factoryf939e41ba3a1151e88f8f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject {
return InputEmailDependency4102766a436592066e97Provider(appComponent: parent1(component) as! AppComponent)
}

#else
extension AppComponent: Registration {
Expand All @@ -186,6 +228,9 @@ extension AppComponent: Registration {
localTable["signupStudentIDFactory-any SignupStudentIDFactory"] = { [unowned self] in self.signupStudentIDFactory as Any }
localTable["signupGenderFactory-any SignupGenderFactory"] = { [unowned self] in self.signupGenderFactory as Any }
localTable["signupCheckLevelFactory-any SignupCheckLevelFactory"] = { [unowned self] in self.signupCheckLevelFactory as Any }
localTable["inputEmailFactory-any InputEmailFactory"] = { [unowned self] in self.inputEmailFactory as Any }
localTable["verifyAuthCodeFactory-any VerifyAuthCodeFactory"] = { [unowned self] in self.verifyAuthCodeFactory as Any }
localTable["inputNewPasswordFactory-any InputNewPasswordFactory"] = { [unowned self] in self.inputNewPasswordFactory as Any }
}
}
extension SplashComponent: Registration {
Expand Down Expand Up @@ -245,6 +290,22 @@ extension RootComponent: Registration {
extension SigninComponent: Registration {
public func registerItems() {
keyPathToName[\SigninDependency.signupEmailFactory] = "signupEmailFactory-any SignupEmailFactory"
keyPathToName[\SigninDependency.inputEmailFactory] = "inputEmailFactory-any InputEmailFactory"
}
}
extension VerifyAuthCodeComponent: Registration {
public func registerItems() {
keyPathToName[\VerifyAuthCodeDependency.inputNewPasswordFactory] = "inputNewPasswordFactory-any InputNewPasswordFactory"
}
}
extension InputNewPasswordComponent: Registration {
public func registerItems() {

}
}
extension InputEmailComponent: Registration {
public func registerItems() {
keyPathToName[\InputEmailDependency.verifyAuthCodeFactory] = "verifyAuthCodeFactory-any VerifyAuthCodeFactory"
}
}

Expand Down Expand Up @@ -275,6 +336,9 @@ private func registerProviderFactory(_ componentPath: String, _ factory: @escapi
registerProviderFactory("^->AppComponent->SignupEmailComponent", factory4d1ddf658c5970ef6b47f47b58f8f304c97af4d5)
registerProviderFactory("^->AppComponent->RootComponent", factory264bfc4d4cb6b0629b40f47b58f8f304c97af4d5)
registerProviderFactory("^->AppComponent->SigninComponent", factory2882a056d84a613debccf47b58f8f304c97af4d5)
registerProviderFactory("^->AppComponent->VerifyAuthCodeComponent", factoryed5ce75de1bf576b84adf47b58f8f304c97af4d5)
registerProviderFactory("^->AppComponent->InputNewPasswordComponent", factory27615059458a0576f404e3b0c44298fc1c149afb)
registerProviderFactory("^->AppComponent->InputEmailComponent", factoryf939e41ba3a1151e88f8f47b58f8f304c97af4d5)
}
#endif

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import SwiftUI

public struct RootPresentationModeKey: EnvironmentKey {
public static let defaultValue: Binding<RootPresentationMode> = .constant(RootPresentationMode())
}

public extension EnvironmentValues {
var rootPresentationMode: Binding<RootPresentationMode> {
get { return self[RootPresentationModeKey.self] }
set { self[RootPresentationModeKey.self] = newValue }
}
}

public typealias RootPresentationMode = Bool

public extension Binding<RootPresentationMode> {
func popToRootView() {
self.wrappedValue.toggle()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import SwiftUI

public protocol InputEmailFactory {
associatedtype SomeView: View
func makeView() -> SomeView
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import SwiftUI

public protocol InputNewPasswordFactory {
associatedtype SomeView: View
func makeView() -> SomeView
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import SwiftUI

public protocol VerifyAuthCodeFactory {
associatedtype SomeView: View
func makeView() -> SomeView
}
17 changes: 17 additions & 0 deletions Projects/Feature/FindPasswordFeature/Project.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import DependencyPlugin
import ProjectDescription
import ProjectDescriptionHelpers

let project = Project.module(
name: ModulePaths.Feature.FindPasswordFeature.rawValue,
targets: [
.interface(module: .feature(.FindPasswordFeature)),
.implements(module: .feature(.FindPasswordFeature), dependencies: [
.feature(target: .FindPasswordFeature, type: .interface),
.feature(target: .BaseFeature)
]),
.tests(module: .feature(.FindPasswordFeature), dependencies: [
.feature(target: .FindPasswordFeature)
])
]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import SwiftUI
import NeedleFoundation
import FindPasswordFeatureInterface

public protocol InputEmailDependency: Dependency {
var verifyAuthCodeFactory: any VerifyAuthCodeFactory { get }
}

public final class InputEmailComponent: Component<InputEmailDependency>, InputEmailFactory {
public func makeView() -> some View {
InputEmailView(
viewModel: .init(),
verifyAuthCodeFactory: dependency.verifyAuthCodeFactory
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import BaseFeature
import Combine

final class InputEmailViewModel: BaseViewModel {
@Published var email: String = ""

@Published var isNavigatedToVerifyAuthCode: Bool = false

func nextButtonDidTap() {
self.isNavigatedToVerifyAuthCode = true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import DesignSystem
import SwiftUI
import BaseFeature
import ViewUtil
import FindPasswordFeatureInterface

struct InputEmailView: View {
private enum FocusField {
case email
}
@FocusState private var focusField: FocusField?
@StateObject var viewModel: InputEmailViewModel
@Environment(\.rootPresentationMode) var rootPresentationMode

private let verifyAuthCodeFactory: any VerifyAuthCodeFactory

init(
viewModel: InputEmailViewModel,
verifyAuthCodeFactory: any VerifyAuthCodeFactory
) {
_viewModel = StateObject(wrappedValue: viewModel)
self.verifyAuthCodeFactory = verifyAuthCodeFactory
}

var body: some View {
VStack(spacing: 0) {
NavigationTitleView(
title: "이메일을 입력해 주세요",
description: "이메일로 인증번호를 전송해 드릴게요"
)

KGTextField(
"이메일을 입력해주세요",
text: $viewModel.email,
title: "이메일",
isError: viewModel.isErrorOccurred,
errorMessage: viewModel.errorMessage,
onCommit: viewModel.nextButtonDidTap
)
.textContentType(.emailAddress)
.keyboardType(.emailAddress)
.focused($focusField, equals: .email)

Spacer()
}
.bottomButton(
text: "다음",
isEditing: focusField != nil,
isDisabled: viewModel.email.isEmpty,
action: viewModel.nextButtonDidTap
)
.navigationBackButton()
.kgBackground()
.hideKeyboardWhenTap()
.navigate(
to: verifyAuthCodeFactory.makeView().eraseToAnyView()
.environment(\.rootPresentationMode, rootPresentationMode),
when: $viewModel.isNavigatedToVerifyAuthCode
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import SwiftUI
import NeedleFoundation
import FindPasswordFeatureInterface

public protocol InputNewPasswordDependency: Dependency {}

public final class InputNewPasswordComponent: Component<InputNewPasswordDependency>, InputNewPasswordFactory {
public func makeView() -> some View {
InputNewPasswordView(
viewModel: .init()
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import DesignSystem
import SwiftUI
import BaseFeature
import SignupFeatureInterface
import ViewUtil

struct InputNewPasswordView: View {
private enum FocusField {
case password
case checkPassword
}
@FocusState private var focusField: FocusField?
@StateObject var viewModel: InputNewPasswordViewModel
@Environment(\.rootPresentationMode) var rootPresentationMode

init(
viewModel: InputNewPasswordViewModel
) {
_viewModel = StateObject(wrappedValue: viewModel)
}

var body: some View {
VStack(spacing: 0) {
NavigationTitleView(
title: "새 비밀번호를 입력해주세요",
description: "비밀번호는 영어와 숫자를 조합해 만들어 주세요"
)

KGTextField(
"비밀번호(8~12자)를 입력해 주세요",
text: $viewModel.password,
title: "비밀번호",
isError: viewModel.isErrorOccurred,
errorMessage: viewModel.errorMessage,
isSecure: true
) {
self.focusField = .checkPassword
}
.textContentType(.password)
.focused($focusField, equals: .password)

KGTextField(
"비밀번호 다시 입력해 주세요",
text: $viewModel.checkPassword,
title: "비밀번호 확인",
isError: viewModel.isErrorOccurred,
errorMessage: viewModel.errorMessage,
isSecure: true,
onCommit: viewModel.nextButtonDidTap
)
.textContentType(.password)
.focused($focusField, equals: .checkPassword)

Spacer()
}
.bottomButton(
text: "다음",
isEditing: focusField != nil,
isDisabled: viewModel.password.isEmpty || viewModel.checkPassword.isEmpty,
action: viewModel.nextButtonDidTap
)
.navigationBar()
.kgBackground()
.hideKeyboardWhenTap()
.onSuccess(of: viewModel.isSuccessToChangePassword) {
DispatchQueue.main.async {
self.rootPresentationMode.popToRootView()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import BaseFeature
import Combine

final class InputNewPasswordViewModel: BaseViewModel {
@Published var password: String = ""
@Published var checkPassword: String = ""

@Published var isSuccessToChangePassword: Bool = false

func nextButtonDidTap() {
self.isSuccessToChangePassword = true
}
}
Loading

0 comments on commit 42bf49a

Please sign in to comment.