diff --git a/Plugin/DependencyPlugin/ProjectDescriptionHelpers/ModulePaths.swift b/Plugin/DependencyPlugin/ProjectDescriptionHelpers/ModulePaths.swift
index 4e1d8af..26a6e22 100644
--- a/Plugin/DependencyPlugin/ProjectDescriptionHelpers/ModulePaths.swift
+++ b/Plugin/DependencyPlugin/ProjectDescriptionHelpers/ModulePaths.swift
@@ -24,6 +24,7 @@ extension ModulePaths: MicroTargetPathConvertable {
public extension ModulePaths {
enum Feature: String, MicroTargetPathConvertable {
+ case FindPasswordFeature
case SignupFeature
case MainFeature
case SigninFeature
diff --git a/Projects/App/Project.swift b/Projects/App/Project.swift
index b87ffe8..6b237ba 100644
--- a/Projects/App/Project.swift
+++ b/Projects/App/Project.swift
@@ -5,7 +5,11 @@ import Foundation
import ProjectDescription
import ProjectDescriptionHelpers
-let configurations: [Configuration] = .default
+let configurations: [Configuration] = [
+ .debug(name: .dev, xcconfig: .relativeToXCConfig(type: .dev, name: env.name)),
+ .debug(name: .stage, xcconfig: .relativeToXCConfig(type: .stage, name: env.name)),
+ .release(name: .prod, xcconfig: .relativeToXCConfig(type: .prod, name: env.name))
+]
let settings: Settings = .settings(
base: env.baseSetting,
diff --git a/Projects/App/Sources/Application/DI/AppComponent.swift b/Projects/App/Sources/Application/DI/AppComponent.swift
index 353b5f1..9f73f4c 100644
--- a/Projects/App/Sources/Application/DI/AppComponent.swift
+++ b/Projects/App/Sources/Application/DI/AppComponent.swift
@@ -10,6 +10,9 @@ import SignupFeature
import SignupFeatureInterface
import MainFeature
import MainFeatureInterface
+import FindPasswordFeature
+import FindPasswordFeatureInterface
+>>>>>>> origin/develop
public final class AppComponent: BootstrapComponent {
// private let _keychain: any Keychain
@@ -41,15 +44,18 @@ public extension AppComponent {
SigninComponent(parent: self)
}
// Signup
- var signupEmailVerifyFactory: any SignupEmailVerifyFactory {
- SignupEmailVerifyComponent(parent: self)
+ var signupEmailFactory: any SignupEmailFactory {
+ SignupEmailComponent(parent: self)
}
- var signupEmailAuthCodeVerifyFactory: any SignupEmailAuthCodeVerifyFactory {
- SignupEmailAuthCodeVerifyComponent(parent: self)
+ var signupVerifyAuthCode: any SignupVerifyAuthCodeFactory {
+ SignupVerifyAuthCodeComponent(parent: self)
}
var signupPasswordFactory: any SignupPasswordFactory {
SignupPasswordComponent(parent: self)
}
+ var signupUserInfoFlowFactory: any SignupUserInfoFlowFactory {
+ SignupUserInfoFlowComponent(parent: self)
+ }
var signupNameFactory: any SignupNameFactory {
SignupNameComponent(parent: self)
}
@@ -64,5 +70,14 @@ public extension AppComponent {
}
var mainFactory: any MainFactory {
MainComponent(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)
}
}
diff --git a/Projects/App/Sources/Application/NeedleGenerated.swift b/Projects/App/Sources/Application/NeedleGenerated.swift
index c9ea2f0..15d4a56 100644
--- a/Projects/App/Sources/Application/NeedleGenerated.swift
+++ b/Projects/App/Sources/Application/NeedleGenerated.swift
@@ -1,7 +1,8 @@
-
import MainFeature
import MainFeatureInterface
+import FindPasswordFeature
+import FindPasswordFeatureInterface
import NeedleFoundation
import RootFeature
import RootFeatureInterface
@@ -37,31 +38,31 @@ private class SplashDependencye0cb7136f2ec3edfd60aProvider: SplashDependency {
private func factoryace9f05f51d68f4c0677e3b0c44298fc1c149afb(_ component: NeedleFoundation.Scope) -> AnyObject {
return SplashDependencye0cb7136f2ec3edfd60aProvider()
}
-private class SignupPasswordDependency778bf5389a70d7df6152Provider: SignupPasswordDependency {
- var signupNameFactory: any SignupNameFactory {
- return appComponent.signupNameFactory
+private class SignupVerifyAuthCodeDependencyc5bfc91282e270b5f35aProvider: SignupVerifyAuthCodeDependency {
+ var signupPasswordFactory: any SignupPasswordFactory {
+ return appComponent.signupPasswordFactory
}
private let appComponent: AppComponent
init(appComponent: AppComponent) {
self.appComponent = appComponent
}
}
-/// ^->AppComponent->SignupPasswordComponent
-private func factorye93d1d56840ff97c674af47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject {
- return SignupPasswordDependency778bf5389a70d7df6152Provider(appComponent: parent1(component) as! AppComponent)
+/// ^->AppComponent->SignupVerifyAuthCodeComponent
+private func factoryff5803dec4bd48820d95f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject {
+ return SignupVerifyAuthCodeDependencyc5bfc91282e270b5f35aProvider(appComponent: parent1(component) as! AppComponent)
}
-private class SignupEmailAuthCodeVerifyDependencyaf9da1ebf0e9e5f1b708Provider: SignupEmailAuthCodeVerifyDependency {
- var signupPasswordFactory: any SignupPasswordFactory {
- return appComponent.signupPasswordFactory
+private class SignupPasswordDependency778bf5389a70d7df6152Provider: SignupPasswordDependency {
+ var signupUserInfoFlowFactory: any SignupUserInfoFlowFactory {
+ return appComponent.signupUserInfoFlowFactory
}
private let appComponent: AppComponent
init(appComponent: AppComponent) {
self.appComponent = appComponent
}
}
-/// ^->AppComponent->SignupEmailAuthCodeVerifyComponent
-private func factoryb06be35aa893adde971bf47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject {
- return SignupEmailAuthCodeVerifyDependencyaf9da1ebf0e9e5f1b708Provider(appComponent: parent1(component) as! AppComponent)
+/// ^->AppComponent->SignupPasswordComponent
+private func factorye93d1d56840ff97c674af47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject {
+ return SignupPasswordDependency778bf5389a70d7df6152Provider(appComponent: parent1(component) as! AppComponent)
}
private class SignupStudentIDDependencyc62f548e51245f5bf42dProvider: SignupStudentIDDependency {
var signupGenderFactory: any SignupGenderFactory {
@@ -77,17 +78,15 @@ private func factorycca12bec445b4f4f6de7f47b58f8f304c97af4d5(_ component: Needle
return SignupStudentIDDependencyc62f548e51245f5bf42dProvider(appComponent: parent1(component) as! AppComponent)
}
private class SignupGenderDependency65e2a6565372eb41f4acProvider: SignupGenderDependency {
- var signupCheckLevelFactory: any SignupCheckLevelFactory {
- return appComponent.signupCheckLevelFactory
- }
- private let appComponent: AppComponent
- init(appComponent: AppComponent) {
- self.appComponent = appComponent
+
+
+ init() {
+
}
}
/// ^->AppComponent->SignupGenderComponent
-private func factory997eaa831d16af15eee5f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject {
- return SignupGenderDependency65e2a6565372eb41f4acProvider(appComponent: parent1(component) as! AppComponent)
+private func factory997eaa831d16af15eee5e3b0c44298fc1c149afb(_ component: NeedleFoundation.Scope) -> AnyObject {
+ return SignupGenderDependency65e2a6565372eb41f4acProvider()
}
private class SignupCheckLevelDependencye4ec84c7d38354a454aeProvider: SignupCheckLevelDependency {
@@ -101,30 +100,50 @@ private func factory5b23127e837152346fdae3b0c44298fc1c149afb(_ component: Needle
return SignupCheckLevelDependencye4ec84c7d38354a454aeProvider()
}
private class SignupNameDependency860534ba64c054146b7bProvider: SignupNameDependency {
+
+
+ init() {
+
+ }
+}
+/// ^->AppComponent->SignupNameComponent
+private func factory71e8fa7f4327b1e25ed0e3b0c44298fc1c149afb(_ component: NeedleFoundation.Scope) -> AnyObject {
+ return SignupNameDependency860534ba64c054146b7bProvider()
+}
+private class SignupUserInfoFlowDependencyc2b570f167242d15d8e8Provider: SignupUserInfoFlowDependency {
+ var signupNameFactory: any SignupNameFactory {
+ return appComponent.signupNameFactory
+ }
var signupStudentIDFactory: any SignupStudentIDFactory {
return appComponent.signupStudentIDFactory
}
+ var signupGenderFactory: any SignupGenderFactory {
+ return appComponent.signupGenderFactory
+ }
+ var signupCheckLevelFactory: any SignupCheckLevelFactory {
+ return appComponent.signupCheckLevelFactory
+ }
private let appComponent: AppComponent
init(appComponent: AppComponent) {
self.appComponent = appComponent
}
}
-/// ^->AppComponent->SignupNameComponent
-private func factory71e8fa7f4327b1e25ed0f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject {
- return SignupNameDependency860534ba64c054146b7bProvider(appComponent: parent1(component) as! AppComponent)
+/// ^->AppComponent->SignupUserInfoFlowComponent
+private func factoryce6f8b9af5f65c8e334ef47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject {
+ return SignupUserInfoFlowDependencyc2b570f167242d15d8e8Provider(appComponent: parent1(component) as! AppComponent)
}
-private class SignupEmailVerifyDependencyf9d372ac752ee19b78caProvider: SignupEmailVerifyDependency {
- var signupEmailAuthCodeVerifyFactory: any SignupEmailAuthCodeVerifyFactory {
- return appComponent.signupEmailAuthCodeVerifyFactory
+private class SignupEmailDependency844c9e928af75b6aaa7aProvider: SignupEmailDependency {
+ var signupVerifyAuthCode: any SignupVerifyAuthCodeFactory {
+ return appComponent.signupVerifyAuthCode
}
private let appComponent: AppComponent
init(appComponent: AppComponent) {
self.appComponent = appComponent
}
}
-/// ^->AppComponent->SignupEmailVerifyComponent
-private func factory3b1904c76335d70151ebf47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject {
- return SignupEmailVerifyDependencyf9d372ac752ee19b78caProvider(appComponent: parent1(component) as! AppComponent)
+/// ^->AppComponent->SignupEmailComponent
+private func factory4d1ddf658c5970ef6b47f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject {
+ return SignupEmailDependency844c9e928af75b6aaa7aProvider(appComponent: parent1(component) as! AppComponent)
}
private class MainDependency7c6a5b4738b211b8e155Provider: MainDependency {
@@ -157,8 +176,11 @@ private func factory264bfc4d4cb6b0629b40f47b58f8f304c97af4d5(_ component: Needle
return RootDependency3944cc797a4a88956fb5Provider(appComponent: parent1(component) as! AppComponent)
}
private class SigninDependencyde06a9d0b22764487733Provider: SigninDependency {
- var signupEmailVerifyFactory: any SignupEmailVerifyFactory {
- return appComponent.signupEmailVerifyFactory
+ var signupEmailFactory: any SignupEmailFactory {
+ return appComponent.signupEmailFactory
+ }
+ var inputEmailFactory: any InputEmailFactory {
+ return appComponent.inputEmailFactory
}
private let appComponent: AppComponent
init(appComponent: AppComponent) {
@@ -169,6 +191,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 {
@@ -176,14 +235,18 @@ extension AppComponent: Registration {
localTable["splashFactory-any SplashFactory"] = { [unowned self] in self.splashFactory as Any }
localTable["signinFactory-any SigninFactory"] = { [unowned self] in self.signinFactory as Any }
- localTable["signupEmailVerifyFactory-any SignupEmailVerifyFactory"] = { [unowned self] in self.signupEmailVerifyFactory as Any }
- localTable["signupEmailAuthCodeVerifyFactory-any SignupEmailAuthCodeVerifyFactory"] = { [unowned self] in self.signupEmailAuthCodeVerifyFactory as Any }
+ localTable["signupEmailFactory-any SignupEmailFactory"] = { [unowned self] in self.signupEmailFactory as Any }
+ localTable["signupVerifyAuthCode-any SignupVerifyAuthCodeFactory"] = { [unowned self] in self.signupVerifyAuthCode as Any }
localTable["signupPasswordFactory-any SignupPasswordFactory"] = { [unowned self] in self.signupPasswordFactory as Any }
+ localTable["signupUserInfoFlowFactory-any SignupUserInfoFlowFactory"] = { [unowned self] in self.signupUserInfoFlowFactory as Any }
localTable["signupNameFactory-any SignupNameFactory"] = { [unowned self] in self.signupNameFactory as Any }
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["mainFactory-any MainFactory"] = { [unowned self] in self.mainFactory 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 {
@@ -191,14 +254,14 @@ extension SplashComponent: Registration {
}
}
-extension SignupPasswordComponent: Registration {
+extension SignupVerifyAuthCodeComponent: Registration {
public func registerItems() {
- keyPathToName[\SignupPasswordDependency.signupNameFactory] = "signupNameFactory-any SignupNameFactory"
+ keyPathToName[\SignupVerifyAuthCodeDependency.signupPasswordFactory] = "signupPasswordFactory-any SignupPasswordFactory"
}
}
-extension SignupEmailAuthCodeVerifyComponent: Registration {
+extension SignupPasswordComponent: Registration {
public func registerItems() {
- keyPathToName[\SignupEmailAuthCodeVerifyDependency.signupPasswordFactory] = "signupPasswordFactory-any SignupPasswordFactory"
+ keyPathToName[\SignupPasswordDependency.signupUserInfoFlowFactory] = "signupUserInfoFlowFactory-any SignupUserInfoFlowFactory"
}
}
extension SignupStudentIDComponent: Registration {
@@ -208,7 +271,7 @@ extension SignupStudentIDComponent: Registration {
}
extension SignupGenderComponent: Registration {
public func registerItems() {
- keyPathToName[\SignupGenderDependency.signupCheckLevelFactory] = "signupCheckLevelFactory-any SignupCheckLevelFactory"
+
}
}
extension SignupCheckLevelComponent: Registration {
@@ -218,12 +281,20 @@ extension SignupCheckLevelComponent: Registration {
}
extension SignupNameComponent: Registration {
public func registerItems() {
- keyPathToName[\SignupNameDependency.signupStudentIDFactory] = "signupStudentIDFactory-any SignupStudentIDFactory"
+
+ }
+}
+extension SignupUserInfoFlowComponent: Registration {
+ public func registerItems() {
+ keyPathToName[\SignupUserInfoFlowDependency.signupNameFactory] = "signupNameFactory-any SignupNameFactory"
+ keyPathToName[\SignupUserInfoFlowDependency.signupStudentIDFactory] = "signupStudentIDFactory-any SignupStudentIDFactory"
+ keyPathToName[\SignupUserInfoFlowDependency.signupGenderFactory] = "signupGenderFactory-any SignupGenderFactory"
+ keyPathToName[\SignupUserInfoFlowDependency.signupCheckLevelFactory] = "signupCheckLevelFactory-any SignupCheckLevelFactory"
}
}
-extension SignupEmailVerifyComponent: Registration {
+extension SignupEmailComponent: Registration {
public func registerItems() {
- keyPathToName[\SignupEmailVerifyDependency.signupEmailAuthCodeVerifyFactory] = "signupEmailAuthCodeVerifyFactory-any SignupEmailAuthCodeVerifyFactory"
+ keyPathToName[\SignupEmailDependency.signupVerifyAuthCode] = "signupVerifyAuthCode-any SignupVerifyAuthCodeFactory"
}
}
extension MainComponent: Registration {
@@ -240,7 +311,23 @@ extension RootComponent: Registration {
}
extension SigninComponent: Registration {
public func registerItems() {
- keyPathToName[\SigninDependency.signupEmailVerifyFactory] = "signupEmailVerifyFactory-any SignupEmailVerifyFactory"
+ 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"
}
}
@@ -261,16 +348,22 @@ private func registerProviderFactory(_ componentPath: String, _ factory: @escapi
@inline(never) private func register1() {
registerProviderFactory("^->AppComponent", factoryEmptyDependencyProvider)
registerProviderFactory("^->AppComponent->SplashComponent", factoryace9f05f51d68f4c0677e3b0c44298fc1c149afb)
+ registerProviderFactory("^->AppComponent->SignupVerifyAuthCodeComponent", factoryff5803dec4bd48820d95f47b58f8f304c97af4d5)
registerProviderFactory("^->AppComponent->SignupPasswordComponent", factorye93d1d56840ff97c674af47b58f8f304c97af4d5)
- registerProviderFactory("^->AppComponent->SignupEmailAuthCodeVerifyComponent", factoryb06be35aa893adde971bf47b58f8f304c97af4d5)
registerProviderFactory("^->AppComponent->SignupStudentIDComponent", factorycca12bec445b4f4f6de7f47b58f8f304c97af4d5)
- registerProviderFactory("^->AppComponent->SignupGenderComponent", factory997eaa831d16af15eee5f47b58f8f304c97af4d5)
+ registerProviderFactory("^->AppComponent->SignupGenderComponent", factory997eaa831d16af15eee5e3b0c44298fc1c149afb)
registerProviderFactory("^->AppComponent->SignupCheckLevelComponent", factory5b23127e837152346fdae3b0c44298fc1c149afb)
registerProviderFactory("^->AppComponent->SignupNameComponent", factory71e8fa7f4327b1e25ed0f47b58f8f304c97af4d5)
registerProviderFactory("^->AppComponent->SignupEmailVerifyComponent", factory3b1904c76335d70151ebf47b58f8f304c97af4d5)
registerProviderFactory("^->AppComponent->MainComponent", factoryc9274e46e78e70f29c54e3b0c44298fc1c149afb)
+ registerProviderFactory("^->AppComponent->SignupNameComponent", factory71e8fa7f4327b1e25ed0e3b0c44298fc1c149afb)
+ registerProviderFactory("^->AppComponent->SignupUserInfoFlowComponent", factoryce6f8b9af5f65c8e334ef47b58f8f304c97af4d5)
+ 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
diff --git a/Projects/App/Support/Info.plist b/Projects/App/Support/Info.plist
index a8624ed..4172bf3 100644
--- a/Projects/App/Support/Info.plist
+++ b/Projects/App/Support/Info.plist
@@ -16,8 +16,8 @@
$(PRODUCT_BUNDLE_PACKAGE_TYPE)
CFBundleShortVersionString
1.0
- CFBundleVersion
- 1
+ CFBundleDisplayName
+ $(BUNDLE_DISPLAY_NAME)
LSRequiresIPhoneOS
UIApplicationSupportsIndirectInputEvents
diff --git a/Projects/Feature/BaseFeature/Sources/EnvironmentValues/RootPresentationMode.swift b/Projects/Feature/BaseFeature/Sources/EnvironmentValues/RootPresentationMode.swift
new file mode 100644
index 0000000..619b788
--- /dev/null
+++ b/Projects/Feature/BaseFeature/Sources/EnvironmentValues/RootPresentationMode.swift
@@ -0,0 +1,20 @@
+import SwiftUI
+
+public struct RootPresentationModeKey: EnvironmentKey {
+ public static let defaultValue: Binding = .constant(RootPresentationMode())
+}
+
+public extension EnvironmentValues {
+ var rootPresentationMode: Binding {
+ get { return self[RootPresentationModeKey.self] }
+ set { self[RootPresentationModeKey.self] = newValue }
+ }
+}
+
+public typealias RootPresentationMode = Bool
+
+public extension Binding {
+ func popToRootView() {
+ self.wrappedValue.toggle()
+ }
+}
diff --git a/Projects/Feature/SignupFeature/Interface/SignupEmailVerifyFactory.swift b/Projects/Feature/FindPasswordFeature/Interface/InputEmailFactory.swift
similarity index 66%
rename from Projects/Feature/SignupFeature/Interface/SignupEmailVerifyFactory.swift
rename to Projects/Feature/FindPasswordFeature/Interface/InputEmailFactory.swift
index e2f140f..ce19a83 100644
--- a/Projects/Feature/SignupFeature/Interface/SignupEmailVerifyFactory.swift
+++ b/Projects/Feature/FindPasswordFeature/Interface/InputEmailFactory.swift
@@ -1,6 +1,6 @@
import SwiftUI
-public protocol SignupEmailVerifyFactory {
+public protocol InputEmailFactory {
associatedtype SomeView: View
func makeView() -> SomeView
}
diff --git a/Projects/Feature/SignupFeature/Interface/SignupEmailAuthCodeVerifyFactory.swift b/Projects/Feature/FindPasswordFeature/Interface/InputNewPasswordFactory.swift
similarity index 62%
rename from Projects/Feature/SignupFeature/Interface/SignupEmailAuthCodeVerifyFactory.swift
rename to Projects/Feature/FindPasswordFeature/Interface/InputNewPasswordFactory.swift
index 12eb80d..f12aab8 100644
--- a/Projects/Feature/SignupFeature/Interface/SignupEmailAuthCodeVerifyFactory.swift
+++ b/Projects/Feature/FindPasswordFeature/Interface/InputNewPasswordFactory.swift
@@ -1,6 +1,6 @@
import SwiftUI
-public protocol SignupEmailAuthCodeVerifyFactory {
+public protocol InputNewPasswordFactory {
associatedtype SomeView: View
func makeView() -> SomeView
}
diff --git a/Projects/Feature/FindPasswordFeature/Interface/VerifyAuthCodeFactory.swift b/Projects/Feature/FindPasswordFeature/Interface/VerifyAuthCodeFactory.swift
new file mode 100644
index 0000000..b977a25
--- /dev/null
+++ b/Projects/Feature/FindPasswordFeature/Interface/VerifyAuthCodeFactory.swift
@@ -0,0 +1,6 @@
+import SwiftUI
+
+public protocol VerifyAuthCodeFactory {
+ associatedtype SomeView: View
+ func makeView() -> SomeView
+}
diff --git a/Projects/Feature/FindPasswordFeature/Project.swift b/Projects/Feature/FindPasswordFeature/Project.swift
new file mode 100644
index 0000000..f654b37
--- /dev/null
+++ b/Projects/Feature/FindPasswordFeature/Project.swift
@@ -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)
+ ])
+ ]
+)
diff --git a/Projects/Feature/FindPasswordFeature/Sources/Email/InputEmailComponent.swift b/Projects/Feature/FindPasswordFeature/Sources/Email/InputEmailComponent.swift
new file mode 100644
index 0000000..b35cc94
--- /dev/null
+++ b/Projects/Feature/FindPasswordFeature/Sources/Email/InputEmailComponent.swift
@@ -0,0 +1,16 @@
+import SwiftUI
+import NeedleFoundation
+import FindPasswordFeatureInterface
+
+public protocol InputEmailDependency: Dependency {
+ var verifyAuthCodeFactory: any VerifyAuthCodeFactory { get }
+}
+
+public final class InputEmailComponent: Component, InputEmailFactory {
+ public func makeView() -> some View {
+ InputEmailView(
+ viewModel: .init(),
+ verifyAuthCodeFactory: dependency.verifyAuthCodeFactory
+ )
+ }
+}
diff --git a/Projects/Feature/FindPasswordFeature/Sources/Email/InputEmailViewModel.swift b/Projects/Feature/FindPasswordFeature/Sources/Email/InputEmailViewModel.swift
new file mode 100644
index 0000000..5831b0f
--- /dev/null
+++ b/Projects/Feature/FindPasswordFeature/Sources/Email/InputEmailViewModel.swift
@@ -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
+ }
+}
diff --git a/Projects/Feature/FindPasswordFeature/Sources/Email/InputSignupEmailView.swift b/Projects/Feature/FindPasswordFeature/Sources/Email/InputSignupEmailView.swift
new file mode 100644
index 0000000..0b44eac
--- /dev/null
+++ b/Projects/Feature/FindPasswordFeature/Sources/Email/InputSignupEmailView.swift
@@ -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
+ )
+ }
+}
diff --git a/Projects/Feature/FindPasswordFeature/Sources/NewPassword/InputNewPasswordComponent.swift b/Projects/Feature/FindPasswordFeature/Sources/NewPassword/InputNewPasswordComponent.swift
new file mode 100644
index 0000000..742f013
--- /dev/null
+++ b/Projects/Feature/FindPasswordFeature/Sources/NewPassword/InputNewPasswordComponent.swift
@@ -0,0 +1,13 @@
+import SwiftUI
+import NeedleFoundation
+import FindPasswordFeatureInterface
+
+public protocol InputNewPasswordDependency: Dependency {}
+
+public final class InputNewPasswordComponent: Component, InputNewPasswordFactory {
+ public func makeView() -> some View {
+ InputNewPasswordView(
+ viewModel: .init()
+ )
+ }
+}
diff --git a/Projects/Feature/FindPasswordFeature/Sources/NewPassword/InputNewPasswordView.swift b/Projects/Feature/FindPasswordFeature/Sources/NewPassword/InputNewPasswordView.swift
new file mode 100644
index 0000000..194f8b9
--- /dev/null
+++ b/Projects/Feature/FindPasswordFeature/Sources/NewPassword/InputNewPasswordView.swift
@@ -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()
+ }
+ }
+ }
+}
diff --git a/Projects/Feature/FindPasswordFeature/Sources/NewPassword/InputNewPasswordViewModel.swift b/Projects/Feature/FindPasswordFeature/Sources/NewPassword/InputNewPasswordViewModel.swift
new file mode 100644
index 0000000..c0aa1a4
--- /dev/null
+++ b/Projects/Feature/FindPasswordFeature/Sources/NewPassword/InputNewPasswordViewModel.swift
@@ -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
+ }
+}
diff --git a/Projects/Feature/FindPasswordFeature/Sources/VerifyAuthCode/VerifyAuthCodeComponent.swift b/Projects/Feature/FindPasswordFeature/Sources/VerifyAuthCode/VerifyAuthCodeComponent.swift
new file mode 100644
index 0000000..d713b57
--- /dev/null
+++ b/Projects/Feature/FindPasswordFeature/Sources/VerifyAuthCode/VerifyAuthCodeComponent.swift
@@ -0,0 +1,17 @@
+import SwiftUI
+import NeedleFoundation
+import FindPasswordFeatureInterface
+
+public protocol VerifyAuthCodeDependency: Dependency {
+ var inputNewPasswordFactory: any InputNewPasswordFactory { get }
+}
+
+public final class VerifyAuthCodeComponent: Component,
+ VerifyAuthCodeFactory {
+ public func makeView() -> some View {
+ VerifyAuthCodeView(
+ viewModel: .init(),
+ inputNewPasswordFactory: dependency.inputNewPasswordFactory
+ )
+ }
+}
diff --git a/Projects/Feature/FindPasswordFeature/Sources/VerifyAuthCode/VerifyAuthCodeView.swift b/Projects/Feature/FindPasswordFeature/Sources/VerifyAuthCode/VerifyAuthCodeView.swift
new file mode 100644
index 0000000..9d0777d
--- /dev/null
+++ b/Projects/Feature/FindPasswordFeature/Sources/VerifyAuthCode/VerifyAuthCodeView.swift
@@ -0,0 +1,70 @@
+import DesignSystem
+import SwiftUI
+import BaseFeature
+import ViewUtil
+import FindPasswordFeatureInterface
+
+struct VerifyAuthCodeView: View {
+ private enum FocusField {
+ case authCode
+ }
+ @FocusState private var focusField: FocusField?
+ @StateObject var viewModel: VerifyAuthCodeViewModel
+ @Environment(\.rootPresentationMode) var rootPresentationMode
+
+ private let inputNewPasswordFactory: any InputNewPasswordFactory
+
+ init(
+ viewModel: VerifyAuthCodeViewModel,
+ inputNewPasswordFactory: any InputNewPasswordFactory
+ ) {
+ _viewModel = StateObject(wrappedValue: viewModel)
+ self.inputNewPasswordFactory = inputNewPasswordFactory
+ }
+
+ var body: some View {
+ VStack(spacing: 0) {
+ NavigationTitleView(
+ title: "인증번호를 입력해 주세요",
+ description: "입력해주신 이메일로 인증번호를 전송해 드렸어요"
+ )
+
+ VerifyCodeTextField(
+ $viewModel.authCode,
+ isError: viewModel.isErrorOccurred,
+ errorMessage: viewModel.errorMessage,
+ onCommit: viewModel.nextButtonDidTap
+ )
+ .focused($focusField, equals: .authCode)
+
+ HStack {
+ Text("5:00")
+ .kgFont(.label, weight: .regular, color: .Greens.main)
+
+ Spacer()
+
+ Text("인증번호 재전송")
+ .kgFont(.label, weight: .regular, color: .Grays.gray400)
+ .underlineText(color: .Grays.gray400)
+ }
+ .padding(.vertical, 16)
+ .padding(.horizontal, 24)
+
+ Spacer()
+ }
+ .bottomButton(
+ text: "인증",
+ isEditing: focusField != nil,
+ isDisabled: viewModel.authCode.count < 4,
+ action: viewModel.nextButtonDidTap
+ )
+ .navigationBackButton()
+ .kgBackground()
+ .hideKeyboardWhenTap()
+ .navigate(
+ to: inputNewPasswordFactory.makeView().eraseToAnyView()
+ .environment(\.rootPresentationMode, rootPresentationMode),
+ when: $viewModel.isNavigatedToNewPassword
+ )
+ }
+}
diff --git a/Projects/Feature/FindPasswordFeature/Sources/VerifyAuthCode/VerifyAuthCodeViewModel.swift b/Projects/Feature/FindPasswordFeature/Sources/VerifyAuthCode/VerifyAuthCodeViewModel.swift
new file mode 100644
index 0000000..9828a4e
--- /dev/null
+++ b/Projects/Feature/FindPasswordFeature/Sources/VerifyAuthCode/VerifyAuthCodeViewModel.swift
@@ -0,0 +1,13 @@
+import BaseFeature
+import Foundation
+import Combine
+
+final class VerifyAuthCodeViewModel: BaseViewModel {
+ @Published var authCode: String = ""
+
+ @Published var isNavigatedToNewPassword: Bool = false
+
+ func nextButtonDidTap() {
+ self.isNavigatedToNewPassword = true
+ }
+}
diff --git a/Projects/Feature/FindPasswordFeature/Tests/FindPasswordFeatureTest.swift b/Projects/Feature/FindPasswordFeature/Tests/FindPasswordFeatureTest.swift
new file mode 100644
index 0000000..ee17017
--- /dev/null
+++ b/Projects/Feature/FindPasswordFeature/Tests/FindPasswordFeatureTest.swift
@@ -0,0 +1,11 @@
+import XCTest
+
+final class FindPasswordFeatureTests: XCTestCase {
+ override func setUpWithError() throws {}
+
+ override func tearDownWithError() throws {}
+
+ func testExample() {
+ XCTAssertEqual(1, 1)
+ }
+}
diff --git a/Projects/Feature/SigninFeature/Project.swift b/Projects/Feature/SigninFeature/Project.swift
index 7bde5f1..048b497 100644
--- a/Projects/Feature/SigninFeature/Project.swift
+++ b/Projects/Feature/SigninFeature/Project.swift
@@ -9,7 +9,8 @@ let project = Project.module(
.implements(module: .feature(.SigninFeature), dependencies: [
.feature(target: .SigninFeature, type: .interface),
.feature(target: .BaseFeature),
- .feature(target: .SignupFeature, type: .interface)
+ .feature(target: .SignupFeature, type: .interface),
+ .feature(target: .FindPasswordFeature, type: .interface)
]),
.tests(module: .feature(.SigninFeature), dependencies: [
.feature(target: .SigninFeature)
diff --git a/Projects/Feature/SigninFeature/Sources/SigninComponent.swift b/Projects/Feature/SigninFeature/Sources/SigninComponent.swift
index 93828b4..1c3e783 100644
--- a/Projects/Feature/SigninFeature/Sources/SigninComponent.swift
+++ b/Projects/Feature/SigninFeature/Sources/SigninComponent.swift
@@ -2,16 +2,19 @@ import SwiftUI
import NeedleFoundation
import SigninFeatureInterface
import SignupFeatureInterface
+import FindPasswordFeatureInterface
public protocol SigninDependency: Dependency {
- var signupEmailVerifyFactory: any SignupEmailVerifyFactory { get }
+ var signupEmailFactory: any SignupEmailFactory { get }
+ var inputEmailFactory: any InputEmailFactory { get }
}
public final class SigninComponent: Component, SigninFactory {
public func makeView() -> some View {
SigninView(
viewModel: .init(),
- signupEmailVerifyFactory: dependency.signupEmailVerifyFactory
+ signupEmailFactory: dependency.signupEmailFactory,
+ inputEmailFactory: dependency.inputEmailFactory
)
}
}
diff --git a/Projects/Feature/SigninFeature/Sources/SigninView.swift b/Projects/Feature/SigninFeature/Sources/SigninView.swift
index fc5c7d5..1a53054 100644
--- a/Projects/Feature/SigninFeature/Sources/SigninView.swift
+++ b/Projects/Feature/SigninFeature/Sources/SigninView.swift
@@ -3,6 +3,7 @@ import SwiftUI
import BaseFeature
import ViewUtil
import SignupFeatureInterface
+import FindPasswordFeatureInterface
struct SigninView: View {
private enum FocusField {
@@ -13,14 +14,17 @@ struct SigninView: View {
@FocusState private var focusField: FocusField?
@StateObject var viewModel: SigninViewModel
- private let signupEmailVerifyFactory: any SignupEmailVerifyFactory
+ private let signupEmailFactory: any SignupEmailFactory
+ private let inputEmailFactory: any InputEmailFactory
init(
viewModel: SigninViewModel,
- signupEmailVerifyFactory: any SignupEmailVerifyFactory
+ signupEmailFactory: any SignupEmailFactory,
+ inputEmailFactory: any InputEmailFactory
) {
_viewModel = StateObject(wrappedValue: viewModel)
- self.signupEmailVerifyFactory = signupEmailVerifyFactory
+ self.signupEmailFactory = signupEmailFactory
+ self.inputEmailFactory = inputEmailFactory
}
var body: some View {
@@ -64,16 +68,31 @@ struct SigninView: View {
Color.Grays.gray700
.frame(width: 1, height: 16)
- Text("비밀번호 찾기")
- .kgFont(.label, weight: .regular, color: .Grays.gray700)
+ Button(action: viewModel.findPasswordButtonDidTap) {
+ Text("비밀번호 찾기")
+ .kgFont(.label, weight: .regular, color: .Grays.gray700)
+ }
}
}
Spacer()
}
+<<<<<<< HEAD
.navigate(to: signupEmailVerifyFactory.makeView().eraseToAnyView(), when: $viewModel.isNavigatedToSignup)
.onChange(of: viewModel.isSuccessToSignin) { _ in
self.appState.sceneFlow = .main
}
+=======
+ .hideKeyboardWhenTap()
+ .navigate(
+ to: signupEmailFactory.makeView().eraseToAnyView(),
+ when: $viewModel.isNavigatedToSignup
+ )
+ .navigate(
+ to: inputEmailFactory.makeView().eraseToAnyView()
+ .environment(\.rootPresentationMode, $viewModel.isNavigatedToFindPassword),
+ when: $viewModel.isNavigatedToFindPassword
+ )
+>>>>>>> origin/develop
}
}
diff --git a/Projects/Feature/SigninFeature/Sources/SigninViewModel.swift b/Projects/Feature/SigninFeature/Sources/SigninViewModel.swift
index 49f4f48..cebe659 100644
--- a/Projects/Feature/SigninFeature/Sources/SigninViewModel.swift
+++ b/Projects/Feature/SigninFeature/Sources/SigninViewModel.swift
@@ -6,6 +6,7 @@ final class SigninViewModel: BaseViewModel {
@Published var password: String = ""
@Published var isSuccessToSignin: Bool = false
@Published var isNavigatedToSignup: Bool = false
+ @Published var isNavigatedToFindPassword: Bool = false
func signinButtonDidTap() {
self.isSuccessToSignin = true
@@ -14,4 +15,8 @@ final class SigninViewModel: BaseViewModel {
func signupButtonDidTap() {
self.isNavigatedToSignup = true
}
+
+ func findPasswordButtonDidTap() {
+ self.isNavigatedToFindPassword = true
+ }
}
diff --git a/Projects/Feature/SignupFeature/Interface/SignupEmailFactory.swift b/Projects/Feature/SignupFeature/Interface/SignupEmailFactory.swift
new file mode 100644
index 0000000..556aafa
--- /dev/null
+++ b/Projects/Feature/SignupFeature/Interface/SignupEmailFactory.swift
@@ -0,0 +1,6 @@
+import SwiftUI
+
+public protocol SignupEmailFactory {
+ associatedtype SomeView: View
+ func makeView() -> SomeView
+}
diff --git a/Projects/Feature/SignupFeature/Interface/SignupVerifyAuthCodeFactory.swift b/Projects/Feature/SignupFeature/Interface/SignupVerifyAuthCodeFactory.swift
new file mode 100644
index 0000000..4689581
--- /dev/null
+++ b/Projects/Feature/SignupFeature/Interface/SignupVerifyAuthCodeFactory.swift
@@ -0,0 +1,6 @@
+import SwiftUI
+
+public protocol SignupVerifyAuthCodeFactory {
+ associatedtype SomeView: View
+ func makeView() -> SomeView
+}
diff --git a/Projects/Feature/SignupFeature/Interface/UserInfo/SignupCheckLevelFactory.swift b/Projects/Feature/SignupFeature/Interface/UserInfo/SignupCheckLevelFactory.swift
index d05004f..8a401fd 100644
--- a/Projects/Feature/SignupFeature/Interface/UserInfo/SignupCheckLevelFactory.swift
+++ b/Projects/Feature/SignupFeature/Interface/UserInfo/SignupCheckLevelFactory.swift
@@ -2,5 +2,5 @@ import SwiftUI
public protocol SignupCheckLevelFactory {
associatedtype SomeView: View
- func makeView() -> SomeView
+ func makeView(nextViewNavigateAction: @escaping () -> Void) -> SomeView
}
diff --git a/Projects/Feature/SignupFeature/Interface/UserInfo/SignupGenderFactory.swift b/Projects/Feature/SignupFeature/Interface/UserInfo/SignupGenderFactory.swift
index a169b8f..e5dce8e 100644
--- a/Projects/Feature/SignupFeature/Interface/UserInfo/SignupGenderFactory.swift
+++ b/Projects/Feature/SignupFeature/Interface/UserInfo/SignupGenderFactory.swift
@@ -2,5 +2,5 @@ import SwiftUI
public protocol SignupGenderFactory {
associatedtype SomeView: View
- func makeView() -> SomeView
+ func makeView(nextViewNavigateAction: @escaping () -> Void) -> SomeView
}
diff --git a/Projects/Feature/SignupFeature/Interface/UserInfo/SignupNameFactory.swift b/Projects/Feature/SignupFeature/Interface/UserInfo/SignupNameFactory.swift
index cffaf15..7823758 100644
--- a/Projects/Feature/SignupFeature/Interface/UserInfo/SignupNameFactory.swift
+++ b/Projects/Feature/SignupFeature/Interface/UserInfo/SignupNameFactory.swift
@@ -2,5 +2,5 @@ import SwiftUI
public protocol SignupNameFactory {
associatedtype SomeView: View
- func makeView() -> SomeView
+ func makeView(nextViewNavigateAction: @escaping () -> Void) -> SomeView
}
diff --git a/Projects/Feature/SignupFeature/Interface/UserInfo/SignupStudentIDFactory.swift b/Projects/Feature/SignupFeature/Interface/UserInfo/SignupStudentIDFactory.swift
index e9b1832..a7d11ed 100644
--- a/Projects/Feature/SignupFeature/Interface/UserInfo/SignupStudentIDFactory.swift
+++ b/Projects/Feature/SignupFeature/Interface/UserInfo/SignupStudentIDFactory.swift
@@ -2,5 +2,5 @@ import SwiftUI
public protocol SignupStudentIDFactory {
associatedtype SomeView: View
- func makeView() -> SomeView
+ func makeView(nextViewNavigateAction: @escaping () -> Void) -> SomeView
}
diff --git a/Projects/Feature/SignupFeature/Interface/UserInfo/SignupUserInfoFlowFactory.swift b/Projects/Feature/SignupFeature/Interface/UserInfo/SignupUserInfoFlowFactory.swift
new file mode 100644
index 0000000..1791c5f
--- /dev/null
+++ b/Projects/Feature/SignupFeature/Interface/UserInfo/SignupUserInfoFlowFactory.swift
@@ -0,0 +1,6 @@
+import SwiftUI
+
+public protocol SignupUserInfoFlowFactory {
+ associatedtype SomeView: View
+ func makeView() -> SomeView
+}
diff --git a/Projects/Feature/SignupFeature/Sources/Email/SignupEmailComponent.swift b/Projects/Feature/SignupFeature/Sources/Email/SignupEmailComponent.swift
new file mode 100644
index 0000000..8db734b
--- /dev/null
+++ b/Projects/Feature/SignupFeature/Sources/Email/SignupEmailComponent.swift
@@ -0,0 +1,16 @@
+import SwiftUI
+import NeedleFoundation
+import SignupFeatureInterface
+
+public protocol SignupEmailDependency: Dependency {
+ var signupVerifyAuthCode: any SignupVerifyAuthCodeFactory { get }
+}
+
+public final class SignupEmailComponent: Component, SignupEmailFactory {
+ public func makeView() -> some View {
+ SignupEmailView(
+ viewModel: .init(),
+ signupVerifyAuthCode: dependency.signupVerifyAuthCode
+ )
+ }
+}
diff --git a/Projects/Feature/SignupFeature/Sources/EmailVerify/SignupEmailVerifyView.swift b/Projects/Feature/SignupFeature/Sources/Email/SignupEmailView.swift
similarity index 71%
rename from Projects/Feature/SignupFeature/Sources/EmailVerify/SignupEmailVerifyView.swift
rename to Projects/Feature/SignupFeature/Sources/Email/SignupEmailView.swift
index d246056..abe8b3d 100644
--- a/Projects/Feature/SignupFeature/Sources/EmailVerify/SignupEmailVerifyView.swift
+++ b/Projects/Feature/SignupFeature/Sources/Email/SignupEmailView.swift
@@ -4,21 +4,21 @@ import BaseFeature
import ViewUtil
import SignupFeatureInterface
-struct SignupEmailVerifyView: View {
+struct SignupEmailView: View {
private enum FocusField {
case email
}
@FocusState private var focusField: FocusField?
- @StateObject var viewModel: SignupEmailVerifyViewModel
+ @StateObject var viewModel: SignupEmailViewModel
- private let signupEmailAuthCodeVerifyFactory: any SignupEmailAuthCodeVerifyFactory
+ private let signupVerifyAuthCode: any SignupVerifyAuthCodeFactory
init(
- viewModel: SignupEmailVerifyViewModel,
- signupEmailAuthCodeVerifyFactory: any SignupEmailAuthCodeVerifyFactory
+ viewModel: SignupEmailViewModel,
+ signupVerifyAuthCode: any SignupVerifyAuthCodeFactory
) {
_viewModel = StateObject(wrappedValue: viewModel)
- self.signupEmailAuthCodeVerifyFactory = signupEmailAuthCodeVerifyFactory
+ self.signupVerifyAuthCode = signupVerifyAuthCode
}
var body: some View {
@@ -52,8 +52,8 @@ struct SignupEmailVerifyView: View {
.kgBackground()
.hideKeyboardWhenTap()
.navigate(
- to: signupEmailAuthCodeVerifyFactory.makeView().eraseToAnyView(),
- when: $viewModel.isNavigatedToEmailAuthCodeVerify
+ to: signupVerifyAuthCode.makeView().eraseToAnyView(),
+ when: $viewModel.isNavigatedToVerifyAuthCode
)
}
}
diff --git a/Projects/Feature/SignupFeature/Sources/Email/SignupEmailViewModel.swift b/Projects/Feature/SignupFeature/Sources/Email/SignupEmailViewModel.swift
new file mode 100644
index 0000000..bcd69c1
--- /dev/null
+++ b/Projects/Feature/SignupFeature/Sources/Email/SignupEmailViewModel.swift
@@ -0,0 +1,12 @@
+import BaseFeature
+import Combine
+
+final class SignupEmailViewModel: BaseViewModel {
+ @Published var email: String = ""
+
+ @Published var isNavigatedToVerifyAuthCode: Bool = false
+
+ func nextButtonDidTap() {
+ self.isNavigatedToVerifyAuthCode = true
+ }
+}
diff --git a/Projects/Feature/SignupFeature/Sources/EmailVerify/SignupEmailVerifyComponent.swift b/Projects/Feature/SignupFeature/Sources/EmailVerify/SignupEmailVerifyComponent.swift
deleted file mode 100644
index 2144fce..0000000
--- a/Projects/Feature/SignupFeature/Sources/EmailVerify/SignupEmailVerifyComponent.swift
+++ /dev/null
@@ -1,16 +0,0 @@
-import SwiftUI
-import NeedleFoundation
-import SignupFeatureInterface
-
-public protocol SignupEmailVerifyDependency: Dependency {
- var signupEmailAuthCodeVerifyFactory: any SignupEmailAuthCodeVerifyFactory { get }
-}
-
-public final class SignupEmailVerifyComponent: Component, SignupEmailVerifyFactory {
- public func makeView() -> some View {
- SignupEmailVerifyView(
- viewModel: .init(),
- signupEmailAuthCodeVerifyFactory: dependency.signupEmailAuthCodeVerifyFactory
- )
- }
-}
diff --git a/Projects/Feature/SignupFeature/Sources/EmailVerify/SignupEmailVerifyViewModel.swift b/Projects/Feature/SignupFeature/Sources/EmailVerify/SignupEmailVerifyViewModel.swift
deleted file mode 100644
index eda6279..0000000
--- a/Projects/Feature/SignupFeature/Sources/EmailVerify/SignupEmailVerifyViewModel.swift
+++ /dev/null
@@ -1,12 +0,0 @@
-import BaseFeature
-import Combine
-
-final class SignupEmailVerifyViewModel: BaseViewModel {
- @Published var email: String = ""
-
- @Published var isNavigatedToEmailAuthCodeVerify: Bool = false
-
- func nextButtonDidTap() {
- self.isNavigatedToEmailAuthCodeVerify = true
- }
-}
diff --git a/Projects/Feature/SignupFeature/Sources/Password/SignupPasswordComponent.swift b/Projects/Feature/SignupFeature/Sources/Password/SignupPasswordComponent.swift
index f60ec80..eb59e0d 100644
--- a/Projects/Feature/SignupFeature/Sources/Password/SignupPasswordComponent.swift
+++ b/Projects/Feature/SignupFeature/Sources/Password/SignupPasswordComponent.swift
@@ -3,14 +3,14 @@ import NeedleFoundation
import SignupFeatureInterface
public protocol SignupPasswordDependency: Dependency {
- var signupNameFactory: any SignupNameFactory { get }
+ var signupUserInfoFlowFactory: any SignupUserInfoFlowFactory { get }
}
public final class SignupPasswordComponent: Component, SignupPasswordFactory {
public func makeView() -> some View {
SignupPasswordView(
viewModel: .init(),
- signupNameFactory: dependency.signupNameFactory
+ signupUserInfoFlowFactory: dependency.signupUserInfoFlowFactory
)
}
}
diff --git a/Projects/Feature/SignupFeature/Sources/Password/SignupPasswordView.swift b/Projects/Feature/SignupFeature/Sources/Password/SignupPasswordView.swift
index a7f14b6..4663b23 100644
--- a/Projects/Feature/SignupFeature/Sources/Password/SignupPasswordView.swift
+++ b/Projects/Feature/SignupFeature/Sources/Password/SignupPasswordView.swift
@@ -12,14 +12,14 @@ struct SignupPasswordView: View {
@FocusState private var focusField: FocusField?
@StateObject var viewModel: SignupPasswordViewModel
- private let signupNameFactory: any SignupNameFactory
+ private let signupUserInfoFlowFactory: any SignupUserInfoFlowFactory
init(
viewModel: SignupPasswordViewModel,
- signupNameFactory: any SignupNameFactory
+ signupUserInfoFlowFactory: any SignupUserInfoFlowFactory
) {
_viewModel = StateObject(wrappedValue: viewModel)
- self.signupNameFactory = signupNameFactory
+ self.signupUserInfoFlowFactory = signupUserInfoFlowFactory
}
var body: some View {
@@ -66,7 +66,7 @@ struct SignupPasswordView: View {
.kgBackground()
.hideKeyboardWhenTap()
.navigate(
- to: signupNameFactory.makeView().eraseToAnyView(),
+ to: signupUserInfoFlowFactory.makeView().eraseToAnyView(),
when: $viewModel.isNavigatedToName
)
}
diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/CheckLevel/SignupCheckLevelComponent.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/CheckLevel/SignupCheckLevelComponent.swift
index f677aec..38ee5a8 100644
--- a/Projects/Feature/SignupFeature/Sources/UserInfo/CheckLevel/SignupCheckLevelComponent.swift
+++ b/Projects/Feature/SignupFeature/Sources/UserInfo/CheckLevel/SignupCheckLevelComponent.swift
@@ -6,9 +6,9 @@ public protocol SignupCheckLevelDependency: Dependency {
}
public final class SignupCheckLevelComponent: Component, SignupCheckLevelFactory {
- public func makeView() -> some View {
+ public func makeView(nextViewNavigateAction: @escaping () -> Void) -> some View {
SignupCheckLevelView(
- viewModel: .init()
+ viewModel: .init(nextViewNavigateAction: nextViewNavigateAction)
)
}
}
diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/CheckLevel/SignupCheckLevelView.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/CheckLevel/SignupCheckLevelView.swift
index 36308c6..bea9ec1 100644
--- a/Projects/Feature/SignupFeature/Sources/UserInfo/CheckLevel/SignupCheckLevelView.swift
+++ b/Projects/Feature/SignupFeature/Sources/UserInfo/CheckLevel/SignupCheckLevelView.swift
@@ -16,8 +16,6 @@ struct SignupCheckLevelView: View {
var body: some View {
VStack(spacing: 0) {
- SignupProgressView(step: .checkLevel)
-
NavigationTitleView(
title: "운동 실력을 알려주세요",
description: "평소 운동을 좋아하고 잘하시는지 알려주세요"
@@ -36,11 +34,9 @@ struct SignupCheckLevelView: View {
Spacer()
}
.bottomButton(
- text: "다음",
-// isDisabled: viewModel.studentID.isEmpty,
+ text: "완료",
action: viewModel.nextButtonDidTap
)
- .navigationBackButton(navigationTitle: "정보 입력")
.kgBackground()
.hideKeyboardWhenTap()
.onChange(of: viewModel.isSuccessToSignup) { _ in
diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/CheckLevel/SignupCheckLevelViewModel.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/CheckLevel/SignupCheckLevelViewModel.swift
index 440ff20..b3913a6 100644
--- a/Projects/Feature/SignupFeature/Sources/UserInfo/CheckLevel/SignupCheckLevelViewModel.swift
+++ b/Projects/Feature/SignupFeature/Sources/UserInfo/CheckLevel/SignupCheckLevelViewModel.swift
@@ -6,7 +6,15 @@ final class SignupCheckLevelViewModel: BaseViewModel {
@Published var selectedLevel: PhysicalLevel = .high
@Published var isSuccessToSignup: Bool = false
+ private let nextViewNavigateAction: () -> Void
+
+ init(nextViewNavigateAction: @escaping () -> Void) {
+ self.nextViewNavigateAction = nextViewNavigateAction
+ }
+
func nextButtonDidTap() {
self.isSuccessToSignup = true
+
+ nextViewNavigateAction()
}
}
diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/Components/SignupProgressView.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/Components/SignupProgressView.swift
index a1a93dc..08d3e84 100644
--- a/Projects/Feature/SignupFeature/Sources/UserInfo/Components/SignupProgressView.swift
+++ b/Projects/Feature/SignupFeature/Sources/UserInfo/Components/SignupProgressView.swift
@@ -32,6 +32,7 @@ struct SignupProgressView: View {
HStack(spacing: 0) {
Color.Greens.secondary
.frame(width: 31, height: 12)
+
ForEach(1...3, id: \.self) { index in
(index < self.step.rawValue ? Color.Greens.secondary : Color.Grays.gray1000)
.frame(height: 12)
@@ -41,7 +42,9 @@ struct SignupProgressView: View {
Color.Grays.gray1000
.frame(width: 31, height: 12)
}
+ .background(Color.Grays.gray1000)
}
.padding(.vertical, 16)
+ .animation(.default, value: step)
}
}
diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/Flow/SignupUserInfoFlowComponent.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/Flow/SignupUserInfoFlowComponent.swift
new file mode 100644
index 0000000..c983fb5
--- /dev/null
+++ b/Projects/Feature/SignupFeature/Sources/UserInfo/Flow/SignupUserInfoFlowComponent.swift
@@ -0,0 +1,22 @@
+import SwiftUI
+import NeedleFoundation
+import SignupFeatureInterface
+
+public protocol SignupUserInfoFlowDependency: Dependency {
+ var signupNameFactory: any SignupNameFactory { get }
+ var signupStudentIDFactory: any SignupStudentIDFactory { get }
+ var signupGenderFactory: any SignupGenderFactory { get }
+ var signupCheckLevelFactory: any SignupCheckLevelFactory { get }
+}
+
+public final class SignupUserInfoFlowComponent: Component, SignupUserInfoFlowFactory {
+ public func makeView() -> some View {
+ SignupUserInfoFlowView(
+ viewModel: .init(),
+ nameFactory: dependency.signupNameFactory,
+ studentIDFactory: dependency.signupStudentIDFactory,
+ genderFactory: dependency.signupGenderFactory,
+ checkLevelFactory: dependency.signupCheckLevelFactory
+ )
+ }
+}
diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/Flow/SignupUserInfoFlowView.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/Flow/SignupUserInfoFlowView.swift
new file mode 100644
index 0000000..864e95e
--- /dev/null
+++ b/Projects/Feature/SignupFeature/Sources/UserInfo/Flow/SignupUserInfoFlowView.swift
@@ -0,0 +1,82 @@
+import SwiftUI
+import DesignSystem
+import ViewUtil
+import SignupFeatureInterface
+
+struct SignupUserInfoFlowView: View {
+ @Environment(\.dismiss) var dismiss
+ @StateObject var viewModel: SignupUserInfoFlowViewModel
+
+ private let nameFactory: any SignupNameFactory
+ private let studentIDFactory: any SignupStudentIDFactory
+ private let genderFactory: any SignupGenderFactory
+ private let checkLevelFactory: any SignupCheckLevelFactory
+
+ init(
+ viewModel: SignupUserInfoFlowViewModel,
+ nameFactory: any SignupNameFactory,
+ studentIDFactory: any SignupStudentIDFactory,
+ genderFactory: any SignupGenderFactory,
+ checkLevelFactory: any SignupCheckLevelFactory
+ ) {
+ _viewModel = StateObject(wrappedValue: viewModel)
+ self.nameFactory = nameFactory
+ self.studentIDFactory = studentIDFactory
+ self.genderFactory = genderFactory
+ self.checkLevelFactory = checkLevelFactory
+ }
+
+ var body: some View {
+ VStack(spacing: 0) {
+ SignupProgressView(step: viewModel.signupStep)
+
+ Group {
+ switch viewModel.signupStep {
+ case .name:
+ nameFactory.makeView(
+ nextViewNavigateAction: viewModel.nextButtonDidTap
+ )
+ .eraseToAnyView()
+
+ case .studentID:
+ studentIDFactory.makeView(
+ nextViewNavigateAction: viewModel.nextButtonDidTap
+ )
+ .eraseToAnyView()
+
+ case .gender:
+ genderFactory.makeView(
+ nextViewNavigateAction: viewModel.nextButtonDidTap
+ )
+ .eraseToAnyView()
+
+ case .checkLevel:
+ checkLevelFactory.makeView(
+ nextViewNavigateAction: viewModel.nextButtonDidTap
+ )
+ .eraseToAnyView()
+ }
+ }
+ .transition(viewModel.transition)
+ }
+ .animation(.default, value: viewModel.signupStep)
+ .navigationBackButton(
+ navigationTitle: "정보 입력",
+ backAction: { viewModel.backButtonDidTap(dismiss: dismiss) }
+ )
+ .kgBackground()
+ .hideKeyboardWhenTap()
+ }
+}
+
+extension AnyTransition {
+ static let backSlideTransition = AnyTransition.asymmetric(
+ insertion: .move(edge: .leading),
+ removal: .move(edge: .trailing)
+ )
+
+ static let nextSlideTransition = AnyTransition.asymmetric(
+ insertion: .move(edge: .trailing),
+ removal: .move(edge: .leading)
+ )
+}
diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/Flow/SignupUserInfoFlowViewModel.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/Flow/SignupUserInfoFlowViewModel.swift
new file mode 100644
index 0000000..d99c071
--- /dev/null
+++ b/Projects/Feature/SignupFeature/Sources/UserInfo/Flow/SignupUserInfoFlowViewModel.swift
@@ -0,0 +1,38 @@
+import BaseFeature
+import Combine
+import SwiftUI
+
+final class SignupUserInfoFlowViewModel: BaseViewModel {
+ @Published var signupStep: SignupStep = .name
+ var transition: AnyTransition = .nextSlideTransition
+
+ func nextButtonDidTap() {
+ self.transition = .nextSlideTransition
+
+ switch signupStep {
+ case .name:
+ self.signupStep = .studentID
+ case .studentID:
+ self.signupStep = .gender
+ case .gender:
+ self.signupStep = .checkLevel
+ case .checkLevel:
+ break
+ }
+ }
+
+ func backButtonDidTap(dismiss: DismissAction) {
+ self.transition = .backSlideTransition
+
+ switch signupStep {
+ case .name:
+ dismiss()
+ case .studentID:
+ self.signupStep = .name
+ case .gender:
+ self.signupStep = .studentID
+ case .checkLevel:
+ self.signupStep = .gender
+ }
+ }
+}
diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/Components/SelectGenderView.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/Components/SelectGenderView.swift
new file mode 100644
index 0000000..f383473
--- /dev/null
+++ b/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/Components/SelectGenderView.swift
@@ -0,0 +1,57 @@
+import SwiftUI
+import DesignSystem
+
+enum Gender {
+ case man
+ case woman
+}
+
+struct SelectGenderView: View {
+ let gender: Gender
+ @Binding var selectedGender: Gender
+
+ init(_ gender: Gender, selectedGender: Binding) {
+ self.gender = gender
+ _selectedGender = selectedGender
+ }
+
+ var body: some View {
+ Button {
+ self.selectedGender = gender
+ } label: {
+ VStack(spacing: 12) {
+ gender.image()
+ .frame(maxWidth: .infinity)
+ .background(Color.Grays.gray1100)
+ .clipCornerRadius(8)
+ .overlay {
+ RoundedRectangle(cornerRadius: 8)
+ .strokeBorder(
+ self.selectedGender == gender ? Color.Greens.main : .clear,
+ lineWidth: 1
+ )
+ }
+ }
+ }
+ }
+}
+
+extension Gender {
+ @ViewBuilder
+ func image() -> some View {
+ switch self {
+ case .man:
+ DesignSystemAsset.Images.man.swiftUIImage
+ .resizable()
+ .frame(width: 130, height: 128)
+ .padding(.vertical, 16)
+
+ case .woman:
+ DesignSystemAsset.Images.woman.swiftUIImage
+ .resizable()
+ .frame(width: 113, height: 144)
+ .padding(.top, 11)
+ .padding(.bottom, 5)
+ }
+ }
+}
diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/SignupGenderComponent.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/SignupGenderComponent.swift
index 7be04c9..97ab0d2 100644
--- a/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/SignupGenderComponent.swift
+++ b/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/SignupGenderComponent.swift
@@ -2,15 +2,12 @@ import SwiftUI
import NeedleFoundation
import SignupFeatureInterface
-public protocol SignupGenderDependency: Dependency {
- var signupCheckLevelFactory: any SignupCheckLevelFactory { get }
-}
+public protocol SignupGenderDependency: Dependency {}
public final class SignupGenderComponent: Component, SignupGenderFactory {
- public func makeView() -> some View {
+ public func makeView(nextViewNavigateAction: @escaping () -> Void) -> some View {
SignupGenderView(
- viewModel: .init(),
- signupCheckLevelFactory: dependency.signupCheckLevelFactory
+ viewModel: .init(nextViewNavigateAction: nextViewNavigateAction)
)
}
}
diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/SignupGenderView.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/SignupGenderView.swift
index 22e1a31..4c2f89f 100644
--- a/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/SignupGenderView.swift
+++ b/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/SignupGenderView.swift
@@ -7,38 +7,33 @@ import ViewUtil
struct SignupGenderView: View {
@StateObject var viewModel: SignupGenderViewModel
- private let signupCheckLevelFactory: any SignupCheckLevelFactory
-
init(
- viewModel: SignupGenderViewModel,
- signupCheckLevelFactory: any SignupCheckLevelFactory
+ viewModel: SignupGenderViewModel
) {
_viewModel = StateObject(wrappedValue: viewModel)
- self.signupCheckLevelFactory = signupCheckLevelFactory
}
var body: some View {
VStack(spacing: 0) {
- SignupProgressView(step: .gender)
-
NavigationTitleView(
title: "성별을 알려주세요",
description: "원활한 서비스를 위해 성별을 알려주세요"
)
+ HStack(spacing: 22) {
+ SelectGenderView(.woman, selectedGender: $viewModel.selectedGender)
+
+ SelectGenderView(.man, selectedGender: $viewModel.selectedGender)
+ }
+ .padding(.horizontal, 24)
+
Spacer()
}
.bottomButton(
text: "다음",
-// isDisabled: viewModel.studentID.isEmpty,
action: viewModel.nextButtonDidTap
)
- .navigationBackButton(navigationTitle: "정보 입력")
.kgBackground()
.hideKeyboardWhenTap()
- .navigate(
- to: signupCheckLevelFactory.makeView().eraseToAnyView(),
- when: $viewModel.isNavigatedToLevelCheck
- )
}
}
diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/SignupGenderViewModel.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/SignupGenderViewModel.swift
index 3203975..ae9d075 100644
--- a/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/SignupGenderViewModel.swift
+++ b/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/SignupGenderViewModel.swift
@@ -2,11 +2,15 @@ import BaseFeature
import Combine
final class SignupGenderViewModel: BaseViewModel {
-// @Published var studentID: String = ""
+ @Published var selectedGender: Gender = .woman
- @Published var isNavigatedToLevelCheck: Bool = false
+ private let nextViewNavigateAction: () -> Void
+
+ init(nextViewNavigateAction: @escaping () -> Void) {
+ self.nextViewNavigateAction = nextViewNavigateAction
+ }
func nextButtonDidTap() {
- self.isNavigatedToLevelCheck = true
+ self.nextViewNavigateAction()
}
}
diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/Name/SignupNameComponent.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/Name/SignupNameComponent.swift
index bb1f68e..19748b3 100644
--- a/Projects/Feature/SignupFeature/Sources/UserInfo/Name/SignupNameComponent.swift
+++ b/Projects/Feature/SignupFeature/Sources/UserInfo/Name/SignupNameComponent.swift
@@ -2,15 +2,12 @@ import SwiftUI
import NeedleFoundation
import SignupFeatureInterface
-public protocol SignupNameDependency: Dependency {
- var signupStudentIDFactory: any SignupStudentIDFactory { get }
-}
+public protocol SignupNameDependency: Dependency {}
public final class SignupNameComponent: Component, SignupNameFactory {
- public func makeView() -> some View {
+ public func makeView(nextViewNavigateAction: @escaping () -> Void) -> some View {
SignupNameView(
- viewModel: .init(),
- signupStudentIDFactory: dependency.signupStudentIDFactory
+ viewModel: .init(nextViewNavigateAction: nextViewNavigateAction)
)
}
}
diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/Name/SignupNameView.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/Name/SignupNameView.swift
index 4884ab0..e469159 100644
--- a/Projects/Feature/SignupFeature/Sources/UserInfo/Name/SignupNameView.swift
+++ b/Projects/Feature/SignupFeature/Sources/UserInfo/Name/SignupNameView.swift
@@ -11,19 +11,14 @@ struct SignupNameView: View {
@FocusState private var focusField: FocusField?
@StateObject var viewModel: SignupNameViewModel
- private let signupStudentIDFactory: any SignupStudentIDFactory
init(
- viewModel: SignupNameViewModel,
- signupStudentIDFactory: any SignupStudentIDFactory
+ viewModel: SignupNameViewModel
) {
_viewModel = StateObject(wrappedValue: viewModel)
- self.signupStudentIDFactory = signupStudentIDFactory
}
var body: some View {
VStack(spacing: 0) {
- SignupProgressView(step: .name)
-
NavigationTitleView(
title: "이름을 입력해 주세요",
description: "원활한 서비스를 위해 이름을 입력해주세요"
@@ -48,12 +43,7 @@ struct SignupNameView: View {
isDisabled: viewModel.name.isEmpty,
action: viewModel.nextButtonDidTap
)
- .navigationBackButton(navigationTitle: "정보 입력")
.kgBackground()
.hideKeyboardWhenTap()
- .navigate(
- to: signupStudentIDFactory.makeView().eraseToAnyView(),
- when: $viewModel.isNavigatedToStudentID
- )
}
}
diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/Name/SignupNameViewModel.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/Name/SignupNameViewModel.swift
index 978ddef..5921ff9 100644
--- a/Projects/Feature/SignupFeature/Sources/UserInfo/Name/SignupNameViewModel.swift
+++ b/Projects/Feature/SignupFeature/Sources/UserInfo/Name/SignupNameViewModel.swift
@@ -4,9 +4,13 @@ import Combine
final class SignupNameViewModel: BaseViewModel {
@Published var name: String = ""
- @Published var isNavigatedToStudentID: Bool = false
+ private let nextViewNavigateAction: () -> Void
+
+ init(nextViewNavigateAction: @escaping () -> Void) {
+ self.nextViewNavigateAction = nextViewNavigateAction
+ }
func nextButtonDidTap() {
- self.isNavigatedToStudentID = true
+ self.nextViewNavigateAction()
}
}
diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/StudentID/SignupStudentIDComponent.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/StudentID/SignupStudentIDComponent.swift
index 004b8f0..615ee67 100644
--- a/Projects/Feature/SignupFeature/Sources/UserInfo/StudentID/SignupStudentIDComponent.swift
+++ b/Projects/Feature/SignupFeature/Sources/UserInfo/StudentID/SignupStudentIDComponent.swift
@@ -7,10 +7,9 @@ public protocol SignupStudentIDDependency: Dependency {
}
public final class SignupStudentIDComponent: Component, SignupStudentIDFactory {
- public func makeView() -> some View {
+ public func makeView(nextViewNavigateAction: @escaping () -> Void) -> some View {
SignupStudentIDView(
- viewModel: .init(),
- signupGenderFactory: dependency.signupGenderFactory
+ viewModel: .init(nextViewNavigateAction: nextViewNavigateAction)
)
}
}
diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/StudentID/SignupStudentIDView.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/StudentID/SignupStudentIDView.swift
index 98cb253..a5a2dd7 100644
--- a/Projects/Feature/SignupFeature/Sources/UserInfo/StudentID/SignupStudentIDView.swift
+++ b/Projects/Feature/SignupFeature/Sources/UserInfo/StudentID/SignupStudentIDView.swift
@@ -11,20 +11,14 @@ struct SignupStudentIDView: View {
@FocusState private var focusField: FocusField?
@StateObject var viewModel: SignupStudentIDViewModel
- private let signupGenderFactory: any SignupGenderFactory
-
init(
- viewModel: SignupStudentIDViewModel,
- signupGenderFactory: any SignupGenderFactory
+ viewModel: SignupStudentIDViewModel
) {
_viewModel = StateObject(wrappedValue: viewModel)
- self.signupGenderFactory = signupGenderFactory
}
var body: some View {
VStack(spacing: 0) {
- SignupProgressView(step: .studentID)
-
NavigationTitleView(
title: "학번을 입력해주세요",
description: "원활한 서비스를 위해 학번을 입력해주세요"
@@ -51,12 +45,7 @@ struct SignupStudentIDView: View {
isDisabled: viewModel.studentID.isEmpty,
action: viewModel.nextButtonDidTap
)
- .navigationBackButton(navigationTitle: "정보 입력")
.kgBackground()
.hideKeyboardWhenTap()
- .navigate(
- to: signupGenderFactory.makeView().eraseToAnyView(),
- when: $viewModel.isNavigatedToGender
- )
}
}
diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/StudentID/SignupStudentIDViewModel.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/StudentID/SignupStudentIDViewModel.swift
index b69ba66..be95a89 100644
--- a/Projects/Feature/SignupFeature/Sources/UserInfo/StudentID/SignupStudentIDViewModel.swift
+++ b/Projects/Feature/SignupFeature/Sources/UserInfo/StudentID/SignupStudentIDViewModel.swift
@@ -4,9 +4,13 @@ import Combine
final class SignupStudentIDViewModel: BaseViewModel {
@Published var studentID: String = ""
- @Published var isNavigatedToGender: Bool = false
+ private let nextViewNavigateAction: () -> Void
+
+ init(nextViewNavigateAction: @escaping () -> Void) {
+ self.nextViewNavigateAction = nextViewNavigateAction
+ }
func nextButtonDidTap() {
- self.isNavigatedToGender = true
+ self.nextViewNavigateAction()
}
}
diff --git a/Projects/Feature/SignupFeature/Sources/EmailAuthCodeVerify/SignupEmailAuthCodeVerifyComponent.swift b/Projects/Feature/SignupFeature/Sources/VerifyAuthCode/SignupVerifyAuthCodeComponent.swift
similarity index 50%
rename from Projects/Feature/SignupFeature/Sources/EmailAuthCodeVerify/SignupEmailAuthCodeVerifyComponent.swift
rename to Projects/Feature/SignupFeature/Sources/VerifyAuthCode/SignupVerifyAuthCodeComponent.swift
index 4ac5e83..9b5a6c2 100644
--- a/Projects/Feature/SignupFeature/Sources/EmailAuthCodeVerify/SignupEmailAuthCodeVerifyComponent.swift
+++ b/Projects/Feature/SignupFeature/Sources/VerifyAuthCode/SignupVerifyAuthCodeComponent.swift
@@ -2,14 +2,14 @@ import SwiftUI
import NeedleFoundation
import SignupFeatureInterface
-public protocol SignupEmailAuthCodeVerifyDependency: Dependency {
+public protocol SignupVerifyAuthCodeDependency: Dependency {
var signupPasswordFactory: any SignupPasswordFactory { get }
}
-public final class SignupEmailAuthCodeVerifyComponent: Component,
- SignupEmailAuthCodeVerifyFactory {
+public final class SignupVerifyAuthCodeComponent: Component,
+ SignupVerifyAuthCodeFactory {
public func makeView() -> some View {
- SignupEmailAuthCodeVerifyView(
+ SignupVerifyAuthCodeView(
viewModel: .init(),
signupPasswordFactory: dependency.signupPasswordFactory
)
diff --git a/Projects/Feature/SignupFeature/Sources/EmailAuthCodeVerify/SignupEmailAuthCodeVerifyView.swift b/Projects/Feature/SignupFeature/Sources/VerifyAuthCode/SignupVerifyAuthCodeView.swift
similarity index 90%
rename from Projects/Feature/SignupFeature/Sources/EmailAuthCodeVerify/SignupEmailAuthCodeVerifyView.swift
rename to Projects/Feature/SignupFeature/Sources/VerifyAuthCode/SignupVerifyAuthCodeView.swift
index 33ee125..1f8c31b 100644
--- a/Projects/Feature/SignupFeature/Sources/EmailAuthCodeVerify/SignupEmailAuthCodeVerifyView.swift
+++ b/Projects/Feature/SignupFeature/Sources/VerifyAuthCode/SignupVerifyAuthCodeView.swift
@@ -4,17 +4,17 @@ import BaseFeature
import ViewUtil
import SignupFeatureInterface
-struct SignupEmailAuthCodeVerifyView: View {
+struct SignupVerifyAuthCodeView: View {
private enum FocusField {
case authCode
}
@FocusState private var focusField: FocusField?
- @StateObject var viewModel: SignupEmailAuthCodeVerifyViewModel
+ @StateObject var viewModel: SignupVerifyAuthCodeViewModel
private let signupPasswordFactory: any SignupPasswordFactory
init(
- viewModel: SignupEmailAuthCodeVerifyViewModel,
+ viewModel: SignupVerifyAuthCodeViewModel,
signupPasswordFactory: any SignupPasswordFactory
) {
_viewModel = StateObject(wrappedValue: viewModel)
@@ -52,7 +52,7 @@ struct SignupEmailAuthCodeVerifyView: View {
Spacer()
}
.bottomButton(
- text: "다음",
+ text: "인증",
isEditing: focusField != nil,
isDisabled: viewModel.authCode.count < 4,
action: viewModel.nextButtonDidTap
diff --git a/Projects/Feature/SignupFeature/Sources/EmailAuthCodeVerify/SignupEmailAuthCodeVerifyViewModel.swift b/Projects/Feature/SignupFeature/Sources/VerifyAuthCode/SignupVerifyAuthCodeViewModel.swift
similarity index 78%
rename from Projects/Feature/SignupFeature/Sources/EmailAuthCodeVerify/SignupEmailAuthCodeVerifyViewModel.swift
rename to Projects/Feature/SignupFeature/Sources/VerifyAuthCode/SignupVerifyAuthCodeViewModel.swift
index 355656a..afc0e0c 100644
--- a/Projects/Feature/SignupFeature/Sources/EmailAuthCodeVerify/SignupEmailAuthCodeVerifyViewModel.swift
+++ b/Projects/Feature/SignupFeature/Sources/VerifyAuthCode/SignupVerifyAuthCodeViewModel.swift
@@ -2,7 +2,7 @@ import BaseFeature
import Foundation
import Combine
-final class SignupEmailAuthCodeVerifyViewModel: BaseViewModel {
+final class SignupVerifyAuthCodeViewModel: BaseViewModel {
@Published var authCode: String = ""
@Published var isNavigatedToPassword: Bool = false
diff --git a/Projects/Shared/ViewUtil/Sources/View+onSuccess.swift b/Projects/Shared/ViewUtil/Sources/View+onSuccess.swift
new file mode 100644
index 0000000..75f369f
--- /dev/null
+++ b/Projects/Shared/ViewUtil/Sources/View+onSuccess.swift
@@ -0,0 +1,11 @@
+import SwiftUI
+
+public extension View {
+ func onSuccess(of value: Bool, _ action: @escaping () -> Void) -> some View {
+ self.onChange(of: value) { changedValue in
+ if changedValue {
+ action()
+ }
+ }
+ }
+}
diff --git a/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Man.imageset/Contents.json b/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Man.imageset/Contents.json
new file mode 100644
index 0000000..5cfbadd
--- /dev/null
+++ b/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Man.imageset/Contents.json
@@ -0,0 +1,24 @@
+{
+ "images" : [
+ {
+ "filename" : "Frame 9576.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Man.imageset/Frame 9576.png b/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Man.imageset/Frame 9576.png
new file mode 100644
index 0000000..8c7fb41
Binary files /dev/null and b/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Man.imageset/Frame 9576.png differ
diff --git a/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Woman.imageset/Contents.json b/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Woman.imageset/Contents.json
new file mode 100644
index 0000000..5d8bf3f
--- /dev/null
+++ b/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Woman.imageset/Contents.json
@@ -0,0 +1,24 @@
+{
+ "images" : [
+ {
+ "filename" : "Frame 9593.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Woman.imageset/Frame 9593.png b/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Woman.imageset/Frame 9593.png
new file mode 100644
index 0000000..8ee17d7
Binary files /dev/null and b/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Woman.imageset/Frame 9593.png differ
diff --git a/Projects/Feature/SignupFeature/Sources/Components/BottomButton.swift b/Projects/UserInterface/DesignSystem/Sources/Extension/View+bottomButton.swift
similarity index 95%
rename from Projects/Feature/SignupFeature/Sources/Components/BottomButton.swift
rename to Projects/UserInterface/DesignSystem/Sources/Extension/View+bottomButton.swift
index 6551884..80eb562 100644
--- a/Projects/Feature/SignupFeature/Sources/Components/BottomButton.swift
+++ b/Projects/UserInterface/DesignSystem/Sources/Extension/View+bottomButton.swift
@@ -1,10 +1,9 @@
import SwiftUI
-import DesignSystem
/// Vstack으로 할 경우 버튼이 잘리는 문제가 발생
/// ZStack을 사용해 SuperView의 상단으로 띄움
-struct BottomButton: ViewModifier {
+private struct BottomButton: ViewModifier {
let text: String
let isEditing: Bool
let isDisabled: Bool
diff --git a/Projects/UserInterface/DesignSystem/Sources/Extension/View+navigationBar.swift b/Projects/UserInterface/DesignSystem/Sources/Extension/View+navigationBar.swift
index 79e5075..a0ff6c5 100644
--- a/Projects/UserInterface/DesignSystem/Sources/Extension/View+navigationBar.swift
+++ b/Projects/UserInterface/DesignSystem/Sources/Extension/View+navigationBar.swift
@@ -7,15 +7,18 @@ public struct NavigationBarModifier: ViewModifier {
private let navigationTitle: String
private let hasBackButton: Bool
+ private let backAction: (() -> Void)?
private let rightIcon: (icon: Icons, action: () -> Void)?
init(
navigationTitle: String,
hasBackButton: Bool = false,
+ backAction: (() -> Void)?,
rightIcon: (Icons, () -> Void)? = nil
) {
self.navigationTitle = navigationTitle
self.hasBackButton = hasBackButton
+ self.backAction = backAction
self.rightIcon = rightIcon
}
@@ -25,7 +28,11 @@ public struct NavigationBarModifier: ViewModifier {
HStack {
if hasBackButton {
Button {
- dismiss()
+ if let backAction {
+ backAction()
+ } else {
+ dismiss()
+ }
} label: {
KGIcon(.chevronLeft)
.frame(28)
@@ -63,24 +70,28 @@ public extension View {
func navigationBar(
navigationTitle: String = "",
hasBackButton: Bool = false,
+ backAction: (() -> Void)? = nil,
rightIcon: (Icons, () -> Void)? = nil
) -> some View {
modifier(
NavigationBarModifier(
navigationTitle: navigationTitle,
hasBackButton: hasBackButton,
+ backAction: backAction,
rightIcon: rightIcon
)
)
}
func navigationBackButton(
- navigationTitle: String = ""
+ navigationTitle: String = "",
+ backAction: (() -> Void)? = nil
) -> some View {
modifier(
NavigationBarModifier(
navigationTitle: navigationTitle,
- hasBackButton: true
+ hasBackButton: true,
+ backAction: backAction
)
)
}
diff --git a/Projects/UserInterface/DesignSystem/Sources/TextField/KGTextField.swift b/Projects/UserInterface/DesignSystem/Sources/TextField/KGTextField.swift
index cea365c..9e19534 100644
--- a/Projects/UserInterface/DesignSystem/Sources/TextField/KGTextField.swift
+++ b/Projects/UserInterface/DesignSystem/Sources/TextField/KGTextField.swift
@@ -69,6 +69,9 @@ public struct KGTextField: View {
lineWidth: 1
)
}
+ .onTapGesture {
+ self.isFocused = true
+ }
if !description.isEmpty || isErrorAndNotEmpty {
Text(isErrorAndNotEmpty ? errorMessage : description)
diff --git a/Projects/UserInterface/DesignSystem/Sources/View/CheckLevelCellView.swift b/Projects/UserInterface/DesignSystem/Sources/View/CheckLevelCellView.swift
index a1c5029..bf6d255 100644
--- a/Projects/UserInterface/DesignSystem/Sources/View/CheckLevelCellView.swift
+++ b/Projects/UserInterface/DesignSystem/Sources/View/CheckLevelCellView.swift
@@ -24,7 +24,7 @@ public struct LevelCheckCellView: View {
.kgFont(.m1, weight: .semiBold, color: .Grays.white)
Text(level.description)
- .kgFont(.m3, weight: .regular, color: .Grays.gray500)
+ .kgFont(.label, weight: .regular, color: .Grays.gray500)
.lineLimit(1)
Spacer()
diff --git a/Projects/Feature/SignupFeature/Sources/Components/NavigationTitleView.swift b/Projects/UserInterface/DesignSystem/Sources/View/NavigationTitleView.swift
similarity index 59%
rename from Projects/Feature/SignupFeature/Sources/Components/NavigationTitleView.swift
rename to Projects/UserInterface/DesignSystem/Sources/View/NavigationTitleView.swift
index 6d6d621..1690ccc 100644
--- a/Projects/Feature/SignupFeature/Sources/Components/NavigationTitleView.swift
+++ b/Projects/UserInterface/DesignSystem/Sources/View/NavigationTitleView.swift
@@ -1,11 +1,15 @@
import SwiftUI
-import DesignSystem
-struct NavigationTitleView: View {
- let title: String
- let description: String
+public struct NavigationTitleView: View {
+ private let title: String
+ private let description: String
- var body: some View {
+ public init(title: String, description: String) {
+ self.title = title
+ self.description = description
+ }
+
+ public var body: some View {
VStack(alignment: .leading, spacing: 8) {
Text(title)
.kgFont(.h3, weight: .semiBold, color: .white)
diff --git a/Tuist/ProjectDescriptionHelpers/GenerateEnvironment.swift b/Tuist/ProjectDescriptionHelpers/GenerateEnvironment.swift
index e282684..8de9e77 100644
--- a/Tuist/ProjectDescriptionHelpers/GenerateEnvironment.swift
+++ b/Tuist/ProjectDescriptionHelpers/GenerateEnvironment.swift
@@ -18,7 +18,7 @@ public extension GenerateEnvironment {
return [.needle]
case .dev:
- return [.swiftLint, .needle]
+ return [.needle]
}
}
var scripts: [TargetScript] {