From 5890b605a8ae1c74fe64ab7473ce75cea2253063 Mon Sep 17 00:00:00 2001 From: HongSJae Date: Tue, 23 Apr 2024 19:21:03 +0900 Subject: [PATCH 01/10] =?UTF-8?q?=E2=9C=A8=20::=20appScripts=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 다른 모듈에서 needle을 script로 가지면 에러가 남(needleGenerated.swift파일이 없어서) --- Projects/App/Project.swift | 2 +- .../GenerateEnvironment.swift | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Projects/App/Project.swift b/Projects/App/Project.swift index ef888f1..b87ffe8 100644 --- a/Projects/App/Project.swift +++ b/Projects/App/Project.swift @@ -13,7 +13,7 @@ let settings: Settings = .settings( defaultSettings: .recommended ) -let scripts: [TargetScript] = generateEnvironment.scripts +let scripts: [TargetScript] = generateEnvironment.appScripts let targets: [Target] = [ .init( diff --git a/Tuist/ProjectDescriptionHelpers/GenerateEnvironment.swift b/Tuist/ProjectDescriptionHelpers/GenerateEnvironment.swift index dc13263..e282684 100644 --- a/Tuist/ProjectDescriptionHelpers/GenerateEnvironment.swift +++ b/Tuist/ProjectDescriptionHelpers/GenerateEnvironment.swift @@ -12,7 +12,7 @@ let environment = ProcessInfo.processInfo.environment["TUIST_ENV"] ?? "" public let generateEnvironment = GenerateEnvironment(rawValue: environment) ?? .dev public extension GenerateEnvironment { - var scripts: [TargetScript] { + var appScripts: [TargetScript] { switch self { case .ci, .cd: return [.needle] @@ -21,4 +21,13 @@ public extension GenerateEnvironment { return [.swiftLint, .needle] } } + var scripts: [TargetScript] { + switch self { + case .ci, .cd: + return [] + + case .dev: + return [.swiftLint] + } + } } From 4f8195ed6a8a6a7388a1601f5cea5455f22b293d Mon Sep 17 00:00:00 2001 From: HongSJae Date: Tue, 23 Apr 2024 19:22:23 +0900 Subject: [PATCH 02/10] =?UTF-8?q?=E2=9C=A8=20::=20Logo=20Image=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Images/Images.xcassets/Contents.json | 6 ++++ .../KillerGram Logo.imageset/Contents.json | 21 +++++++++++++ .../KillerGram Logo.svg | 3 ++ .../DesignSystem/Sources/Images/KGImage.swift | 31 +++++++++++++++++++ 4 files changed, 61 insertions(+) create mode 100644 Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Contents.json create mode 100644 Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/KillerGram Logo.imageset/Contents.json create mode 100644 Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/KillerGram Logo.imageset/KillerGram Logo.svg create mode 100644 Projects/UserInterface/DesignSystem/Sources/Images/KGImage.swift diff --git a/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Contents.json b/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/KillerGram Logo.imageset/Contents.json b/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/KillerGram Logo.imageset/Contents.json new file mode 100644 index 0000000..1d7015f --- /dev/null +++ b/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/KillerGram Logo.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "KillerGram Logo.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/KillerGram Logo.imageset/KillerGram Logo.svg b/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/KillerGram Logo.imageset/KillerGram Logo.svg new file mode 100644 index 0000000..d9a2a38 --- /dev/null +++ b/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/KillerGram Logo.imageset/KillerGram Logo.svg @@ -0,0 +1,3 @@ + + + diff --git a/Projects/UserInterface/DesignSystem/Sources/Images/KGImage.swift b/Projects/UserInterface/DesignSystem/Sources/Images/KGImage.swift new file mode 100644 index 0000000..f61b80b --- /dev/null +++ b/Projects/UserInterface/DesignSystem/Sources/Images/KGImage.swift @@ -0,0 +1,31 @@ +import SwiftUI + +public struct KGImage: View { + public enum Image { + case logo + } + + private var image: Image + private var renderingMode: SwiftUI.Image.TemplateRenderingMode + + public init( + _ image: Image, + renderingMode: SwiftUI.Image.TemplateRenderingMode = .original + ) { + self.image = image + self.renderingMode = renderingMode + } + + public var body: some View { + jobisToImage() + .resizable() + .renderingMode(renderingMode) + } + + private func jobisToImage() -> SwiftUI.Image { + switch image { + case .logo: + DesignSystemAsset.Images.killerGramLogo.swiftUIImage + } + } +} From dc69fe2860ca5890baf772faad5090126375a471 Mon Sep 17 00:00:00 2001 From: HongSJae Date: Tue, 23 Apr 2024 19:24:43 +0900 Subject: [PATCH 03/10] =?UTF-8?q?=E2=9C=A8=20::=20ViewUtil=20Module=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit eraseToAnyView --- Projects/Shared/ViewUtil/Project.swift | 10 ++++++++++ .../Shared/ViewUtil/Sources/View+eraseToAnyView.swift | 7 +++++++ 2 files changed, 17 insertions(+) create mode 100644 Projects/Shared/ViewUtil/Project.swift create mode 100644 Projects/Shared/ViewUtil/Sources/View+eraseToAnyView.swift diff --git a/Projects/Shared/ViewUtil/Project.swift b/Projects/Shared/ViewUtil/Project.swift new file mode 100644 index 0000000..5ed101d --- /dev/null +++ b/Projects/Shared/ViewUtil/Project.swift @@ -0,0 +1,10 @@ +import DependencyPlugin +import ProjectDescription +import ProjectDescriptionHelpers + +let project = Project.module( + name: ModulePaths.Shared.ViewUtil.rawValue, + targets: [ + .implements(module: .shared(.ViewUtil)) + ] +) diff --git a/Projects/Shared/ViewUtil/Sources/View+eraseToAnyView.swift b/Projects/Shared/ViewUtil/Sources/View+eraseToAnyView.swift new file mode 100644 index 0000000..0921b34 --- /dev/null +++ b/Projects/Shared/ViewUtil/Sources/View+eraseToAnyView.swift @@ -0,0 +1,7 @@ +import SwiftUI + +public extension View { + func eraseToAnyView() -> AnyView { + AnyView(self) + } +} From 62572fa326c4376151ddb6f45cea9fd544fa9aa0 Mon Sep 17 00:00:00 2001 From: HongSJae Date: Tue, 23 Apr 2024 19:25:53 +0900 Subject: [PATCH 04/10] =?UTF-8?q?=E2=9C=A8=20::=20Splash=20Publishing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ModulePaths.swift | 2 ++ .../Sources/Application/DI/AppComponent.swift | 5 ++++ .../Interface/SplashFactory.swift | 6 ++++ Projects/Feature/SplashFeature/Project.swift | 17 +++++++++++ .../Sources/SplashComponent.swift | 13 ++++++++ .../SplashFeature/Sources/SplashView.swift | 30 +++++++++++++++++++ .../Sources/SplashViewModel.swift | 18 +++++++++++ .../Tests/SplashFeatureTest.swift | 11 +++++++ .../UserInterface/DesignSystem/Project.swift | 9 ++---- 9 files changed, 105 insertions(+), 6 deletions(-) create mode 100644 Projects/Feature/SplashFeature/Interface/SplashFactory.swift create mode 100644 Projects/Feature/SplashFeature/Project.swift create mode 100644 Projects/Feature/SplashFeature/Sources/SplashComponent.swift create mode 100644 Projects/Feature/SplashFeature/Sources/SplashView.swift create mode 100644 Projects/Feature/SplashFeature/Sources/SplashViewModel.swift create mode 100644 Projects/Feature/SplashFeature/Tests/SplashFeatureTest.swift diff --git a/Plugin/DependencyPlugin/ProjectDescriptionHelpers/ModulePaths.swift b/Plugin/DependencyPlugin/ProjectDescriptionHelpers/ModulePaths.swift index 5878582..a4ff44f 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 SplashFeature case RootFeature case BaseFeature } @@ -43,6 +44,7 @@ public extension ModulePaths { public extension ModulePaths { enum Shared: String, MicroTargetPathConvertable { + case ViewUtil case GlobalThirdPartyLibrary } } diff --git a/Projects/App/Sources/Application/DI/AppComponent.swift b/Projects/App/Sources/Application/DI/AppComponent.swift index 5f6515d..bd5f0b3 100644 --- a/Projects/App/Sources/Application/DI/AppComponent.swift +++ b/Projects/App/Sources/Application/DI/AppComponent.swift @@ -2,6 +2,8 @@ import NeedleFoundation import SwiftUI import RootFeature import RootFeatureInterface +import SplashFeature +import SplashFeatureInterface public final class AppComponent: BootstrapComponent { // private let _keychain: any Keychain @@ -26,4 +28,7 @@ public final class AppComponent: BootstrapComponent { } public extension AppComponent { + var splashFactory: any SplashFactory { + SplashComponent(parent: self) + } } diff --git a/Projects/Feature/SplashFeature/Interface/SplashFactory.swift b/Projects/Feature/SplashFeature/Interface/SplashFactory.swift new file mode 100644 index 0000000..39d324e --- /dev/null +++ b/Projects/Feature/SplashFeature/Interface/SplashFactory.swift @@ -0,0 +1,6 @@ +import SwiftUI + +public protocol SplashFactory { + associatedtype SomeView: View + func makeView() -> SomeView +} diff --git a/Projects/Feature/SplashFeature/Project.swift b/Projects/Feature/SplashFeature/Project.swift new file mode 100644 index 0000000..8a0dcda --- /dev/null +++ b/Projects/Feature/SplashFeature/Project.swift @@ -0,0 +1,17 @@ +import DependencyPlugin +import ProjectDescription +import ProjectDescriptionHelpers + +let project = Project.module( + name: ModulePaths.Feature.SplashFeature.rawValue, + targets: [ + .interface(module: .feature(.SplashFeature)), + .implements(module: .feature(.SplashFeature), dependencies: [ + .feature(target: .SplashFeature, type: .interface), + .feature(target: .BaseFeature) + ]), + .tests(module: .feature(.SplashFeature), dependencies: [ + .feature(target: .SplashFeature) + ]) + ] +) diff --git a/Projects/Feature/SplashFeature/Sources/SplashComponent.swift b/Projects/Feature/SplashFeature/Sources/SplashComponent.swift new file mode 100644 index 0000000..3f5d31e --- /dev/null +++ b/Projects/Feature/SplashFeature/Sources/SplashComponent.swift @@ -0,0 +1,13 @@ +import SwiftUI +import NeedleFoundation +import SplashFeatureInterface +public protocol SplashDependency: Dependency { +} + +public final class SplashComponent: Component, SplashFactory { + public func makeView() -> some View { + SplashView( + viewModel: .init() + ) + } +} diff --git a/Projects/Feature/SplashFeature/Sources/SplashView.swift b/Projects/Feature/SplashFeature/Sources/SplashView.swift new file mode 100644 index 0000000..a321093 --- /dev/null +++ b/Projects/Feature/SplashFeature/Sources/SplashView.swift @@ -0,0 +1,30 @@ +import DesignSystem +import SwiftUI +import BaseFeature + +struct SplashView: View { + @StateObject var viewModel: SplashViewModel + @EnvironmentObject var appState: AppState + + init( + viewModel: SplashViewModel + ) { + _viewModel = StateObject(wrappedValue: viewModel) + } + + var body: some View { + VStack { + KGImage(.logo) + .frame(width: 254, height: 53) + } + .frame(maxWidth: .infinity, maxHeight: /*@START_MENU_TOKEN@*/.infinity/*@END_MENU_TOKEN@*/) + .background(Color.System.background.ignoresSafeArea()) + .onAppear { + // viewModel.onAppear { + // appState.sceneFlow = .main + // } onError: { _ in + // appState.sceneFlow = .auth + // } + } + } +} diff --git a/Projects/Feature/SplashFeature/Sources/SplashViewModel.swift b/Projects/Feature/SplashFeature/Sources/SplashViewModel.swift new file mode 100644 index 0000000..2af3930 --- /dev/null +++ b/Projects/Feature/SplashFeature/Sources/SplashViewModel.swift @@ -0,0 +1,18 @@ +import BaseFeature +import Combine + +final class SplashViewModel: BaseViewModel { + func onAppear( + onSuccess: @escaping () -> Void, + onError: @escaping (Error) -> Void + ) { + // 자동로그인 + if true { + // 토큰이 되면 -> 메인 뷰로 + onSuccess() + } else { + // 안되면 -> 로그인 뷰로 +// onError() + } + } +} diff --git a/Projects/Feature/SplashFeature/Tests/SplashFeatureTest.swift b/Projects/Feature/SplashFeature/Tests/SplashFeatureTest.swift new file mode 100644 index 0000000..54f94b1 --- /dev/null +++ b/Projects/Feature/SplashFeature/Tests/SplashFeatureTest.swift @@ -0,0 +1,11 @@ +import XCTest + +final class SplashFeatureTests: XCTestCase { + override func setUpWithError() throws {} + + override func tearDownWithError() throws {} + + func testExample() { + XCTAssertEqual(1, 1) + } +} diff --git a/Projects/UserInterface/DesignSystem/Project.swift b/Projects/UserInterface/DesignSystem/Project.swift index 1a8dd62..8eb1ab1 100644 --- a/Projects/UserInterface/DesignSystem/Project.swift +++ b/Projects/UserInterface/DesignSystem/Project.swift @@ -13,12 +13,9 @@ let project = Project.module( ), .demo( module: .userInterface(.DesignSystem), - spec: .init( - scripts: scripts, - dependencies: [ - .userInterface(target: .DesignSystem) - ] - ) + dependencies: [ + .userInterface(target: .DesignSystem) + ] ) ] ) From c1dc2d90e386dc7a1069916ecfd7b48919c9a319 Mon Sep 17 00:00:00 2001 From: HongSJae Date: Tue, 23 Apr 2024 19:26:56 +0900 Subject: [PATCH 05/10] =?UTF-8?q?=E2=9C=A8=20::=20DI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .swiftlint.yml | 3 + .../Sources/Application/NeedleGenerated.swift | 91 +++++++++++++++++++ .../RootFeature/Sources/RootComponent.swift | 9 +- 3 files changed, 97 insertions(+), 6 deletions(-) create mode 100644 .swiftlint.yml create mode 100644 Projects/App/Sources/Application/NeedleGenerated.swift diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 0000000..8b86bfd --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,3 @@ +excluded: + - "**/*/NeedleGenerated.swift" + - "Tuist" diff --git a/Projects/App/Sources/Application/NeedleGenerated.swift b/Projects/App/Sources/Application/NeedleGenerated.swift new file mode 100644 index 0000000..147b513 --- /dev/null +++ b/Projects/App/Sources/Application/NeedleGenerated.swift @@ -0,0 +1,91 @@ + + +import NeedleFoundation +import RootFeature +import RootFeatureInterface +import SplashFeature +import SplashFeatureInterface +import SwiftUI + +// swiftlint:disable unused_declaration +private let needleDependenciesHash : String? = nil + +// MARK: - Traversal Helpers + +private func parent1(_ component: NeedleFoundation.Scope) -> NeedleFoundation.Scope { + return component.parent +} + +// MARK: - Providers + +#if !NEEDLE_DYNAMIC + +private class SplashDependencye0cb7136f2ec3edfd60aProvider: SplashDependency { + + + init() { + + } +} +/// ^->AppComponent->SplashComponent +private func factoryace9f05f51d68f4c0677e3b0c44298fc1c149afb(_ component: NeedleFoundation.Scope) -> AnyObject { + return SplashDependencye0cb7136f2ec3edfd60aProvider() +} +private class RootDependency3944cc797a4a88956fb5Provider: RootDependency { + var splashFactory: any SplashFactory { + return appComponent.splashFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->RootComponent +private func factory264bfc4d4cb6b0629b40f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return RootDependency3944cc797a4a88956fb5Provider(appComponent: parent1(component) as! AppComponent) +} + +#else +extension AppComponent: Registration { + public func registerItems() { + + localTable["splashFactory-any SplashFactory"] = { [unowned self] in self.splashFactory as Any } + } +} +extension SplashComponent: Registration { + public func registerItems() { + + } +} +extension RootComponent: Registration { + public func registerItems() { + keyPathToName[\RootDependency.splashFactory] = "splashFactory-any SplashFactory" + } +} + + +#endif + +private func factoryEmptyDependencyProvider(_ component: NeedleFoundation.Scope) -> AnyObject { + return EmptyDependencyProvider(component: component) +} + +// MARK: - Registration +private func registerProviderFactory(_ componentPath: String, _ factory: @escaping (NeedleFoundation.Scope) -> AnyObject) { + __DependencyProviderRegistry.instance.registerDependencyProviderFactory(for: componentPath, factory) +} + +#if !NEEDLE_DYNAMIC + +@inline(never) private func register1() { + registerProviderFactory("^->AppComponent", factoryEmptyDependencyProvider) + registerProviderFactory("^->AppComponent->SplashComponent", factoryace9f05f51d68f4c0677e3b0c44298fc1c149afb) + registerProviderFactory("^->AppComponent->RootComponent", factory264bfc4d4cb6b0629b40f47b58f8f304c97af4d5) +} +#endif + +public func registerProviderFactories() { +#if !NEEDLE_DYNAMIC + register1() +#endif +} diff --git a/Projects/Feature/RootFeature/Sources/RootComponent.swift b/Projects/Feature/RootFeature/Sources/RootComponent.swift index 85ebcce..cfc1397 100644 --- a/Projects/Feature/RootFeature/Sources/RootComponent.swift +++ b/Projects/Feature/RootFeature/Sources/RootComponent.swift @@ -1,19 +1,16 @@ import RootFeatureInterface import NeedleFoundation import SwiftUI +import SplashFeatureInterface public protocol RootDependency: Dependency { -// var signinFactory: any SigninFactory { get } -// var splashFactory: any SplashFactory { get } -// var mainFactory: any MainFactory { get } + var splashFactory: any SplashFactory { get } } public final class RootComponent: Component, RootFactory { public func makeView() -> some View { RootView( -// signinFactory: dependency.signinFactory, -// splashFactory: dependency.splashFactory, -// mainFactory: dependency.mainFactory + splashFactory: dependency.splashFactory ) } } From 0b1c382bf8ebefdc2eff344f45d8cc19a828e2f1 Mon Sep 17 00:00:00 2001 From: HongSJae Date: Tue, 23 Apr 2024 19:27:18 +0900 Subject: [PATCH 06/10] =?UTF-8?q?=E2=9C=A8=20::=20Splash=20Flow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/App/Resources/LaunchScreen.storyboard | 18 +++++++++++++----- .../Sources/Application/KillerGramApp.swift | 13 ++++++++----- Projects/Feature/BaseFeature/Project.swift | 3 ++- .../Feature/BaseFeature/Sources/AppState.swift | 9 +++++++++ 4 files changed, 32 insertions(+), 11 deletions(-) create mode 100644 Projects/Feature/BaseFeature/Sources/AppState.swift diff --git a/Projects/App/Resources/LaunchScreen.storyboard b/Projects/App/Resources/LaunchScreen.storyboard index 865e932..b613293 100644 --- a/Projects/App/Resources/LaunchScreen.storyboard +++ b/Projects/App/Resources/LaunchScreen.storyboard @@ -1,7 +1,10 @@ - - + + + - + + + @@ -11,10 +14,10 @@ - + - + @@ -22,4 +25,9 @@ + + + + + diff --git a/Projects/App/Sources/Application/KillerGramApp.swift b/Projects/App/Sources/Application/KillerGramApp.swift index 07d0aa2..2706f34 100644 --- a/Projects/App/Sources/Application/KillerGramApp.swift +++ b/Projects/App/Sources/Application/KillerGramApp.swift @@ -1,19 +1,22 @@ import SwiftUI import DesignSystem +import BaseFeature +import ViewUtil @main struct KillerGramApp: App { + @StateObject var appState = AppState(sceneFlow: .splash) + init() { DesignSystemFontFamily.registerAllCustomFonts() -// registerProviderFactori이es() + registerProviderFactories() } var body: some Scene { WindowGroup { - AppComponent() - .makeRootView() -// .eraseToAnyView() -// .environmentObject(appState) + AppComponent().makeRootView() + .eraseToAnyView() + .environmentObject(appState) } } } diff --git a/Projects/Feature/BaseFeature/Project.swift b/Projects/Feature/BaseFeature/Project.swift index f9d7b6a..8e70199 100644 --- a/Projects/Feature/BaseFeature/Project.swift +++ b/Projects/Feature/BaseFeature/Project.swift @@ -7,7 +7,8 @@ let project = Project.module( targets: [ .implements(module: .feature(.BaseFeature), dependencies: [ .userInterface(target: .DesignSystem), - .shared(target: .GlobalThirdPartyLibrary) + .shared(target: .GlobalThirdPartyLibrary), + .shared(target: .ViewUtil) ]), .tests(module: .feature(.BaseFeature), dependencies: [ .feature(target: .BaseFeature) diff --git a/Projects/Feature/BaseFeature/Sources/AppState.swift b/Projects/Feature/BaseFeature/Sources/AppState.swift new file mode 100644 index 0000000..eac627c --- /dev/null +++ b/Projects/Feature/BaseFeature/Sources/AppState.swift @@ -0,0 +1,9 @@ +import Foundation + +public final class AppState: ObservableObject { + @Published public var sceneFlow: SceneFlow + + public init(sceneFlow: SceneFlow) { + self.sceneFlow = sceneFlow + } +} From 6c8ea58fc201e976c4bd225c26a361bc25c8601f Mon Sep 17 00:00:00 2001 From: HongSJae Date: Tue, 23 Apr 2024 19:27:34 +0900 Subject: [PATCH 07/10] =?UTF-8?q?=E2=9C=A8=20::=20Root=20Flow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BaseFeature/Sources/BaseViewModel.swift | 31 +++++++++++ .../Feature/BaseFeature/Sources/Feature.swift | 1 - .../BaseFeature/Sources/SceneFlow.swift | 7 +++ .../RootFeature/Sources/RootView.swift | 54 +++++++++---------- 4 files changed, 62 insertions(+), 31 deletions(-) create mode 100644 Projects/Feature/BaseFeature/Sources/BaseViewModel.swift delete mode 100644 Projects/Feature/BaseFeature/Sources/Feature.swift create mode 100644 Projects/Feature/BaseFeature/Sources/SceneFlow.swift diff --git a/Projects/Feature/BaseFeature/Sources/BaseViewModel.swift b/Projects/Feature/BaseFeature/Sources/BaseViewModel.swift new file mode 100644 index 0000000..c8228ff --- /dev/null +++ b/Projects/Feature/BaseFeature/Sources/BaseViewModel.swift @@ -0,0 +1,31 @@ +import SwiftUI + +open class BaseViewModel: ObservableObject { + @Published public var isErrorOccurred = false + @Published public var isLoading = false + @Published public var errorMessage = "" + + public init() {} + + public func addCancellable( + _ task: @escaping @Sendable () async throws -> T, + onReceiveValue: @escaping (T) -> Void, + onReceiveError: ((Error) -> Void)? = nil + ) { + isLoading = true + Task { + do { + let value = try await task() + onReceiveValue(value) + } catch { + if let onReceiveError { + onReceiveError(error) + } + + errorMessage = error.localizedDescription + isErrorOccurred = true + } + isLoading = false + } + } +} diff --git a/Projects/Feature/BaseFeature/Sources/Feature.swift b/Projects/Feature/BaseFeature/Sources/Feature.swift deleted file mode 100644 index 7d846fc..0000000 --- a/Projects/Feature/BaseFeature/Sources/Feature.swift +++ /dev/null @@ -1 +0,0 @@ -// this is for tuist diff --git a/Projects/Feature/BaseFeature/Sources/SceneFlow.swift b/Projects/Feature/BaseFeature/Sources/SceneFlow.swift new file mode 100644 index 0000000..8151f35 --- /dev/null +++ b/Projects/Feature/BaseFeature/Sources/SceneFlow.swift @@ -0,0 +1,7 @@ +import Foundation + +public enum SceneFlow: String, RawRepresentable { + case splash + case auth + case main +} diff --git a/Projects/Feature/RootFeature/Sources/RootView.swift b/Projects/Feature/RootFeature/Sources/RootView.swift index 53ae252..aec5da4 100644 --- a/Projects/Feature/RootFeature/Sources/RootView.swift +++ b/Projects/Feature/RootFeature/Sources/RootView.swift @@ -1,42 +1,36 @@ import BaseFeature import SwiftUI - import DesignSystem +import SplashFeatureInterface +import ViewUtil struct RootView: View { -// @EnvironmentObject var appState: AppState -// private let signinFactory: any SigninFactory -// private let splashFactory: any SplashFactory -// private let mainFactory: any MainFactory -// -// public init( -// signinFactory: any SigninFactory, -// splashFactory: any SplashFactory, -// mainFactory: any MainFactory -// ) { -// self.signinFactory = signinFactory -// self.splashFactory = splashFactory -// self.mainFactory = mainFactory -// } + @EnvironmentObject var appState: AppState + private let splashFactory: any SplashFactory + + public init( + splashFactory: any SplashFactory + ) { + self.splashFactory = splashFactory + } var body: some View { ZStack { -// switch appState.sceneFlow { -// case .auth: -// signinFactory.makeView().eraseToAnyView() -// .environmentObject(appState) -// -// case .main: -// mainFactory.makeView().eraseToAnyView() -// .environmentObject(appState) -// -// case .splash: -// splashFactory.makeView().eraseToAnyView() -// .environmentObject(appState) -// } - Text("RootView") + switch appState.sceneFlow { + case .auth: + EmptyView() + .environmentObject(appState) + + case .main: + EmptyView() + .environmentObject(appState) + + case .splash: + splashFactory.makeView().eraseToAnyView() + .environmentObject(appState) + } } -// .animation(.easeInOut, value: appState.sceneFlow) + .animation(.easeInOut, value: appState.sceneFlow) .transition(.opacity.animation(.easeInOut)) } } From 97e7f4e88a1bc50a67703906d2850c9af3d6d15b Mon Sep 17 00:00:00 2001 From: HongSJae Date: Tue, 23 Apr 2024 20:28:43 +0900 Subject: [PATCH 08/10] =?UTF-8?q?=E2=9C=A8=20::=20Signin=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ModulePaths.swift | 1 + .../Sources/Application/DI/AppComponent.swift | 5 ++++ .../Sources/Application/NeedleGenerated.swift | 24 +++++++++++++++++++ .../RootFeature/Sources/RootComponent.swift | 5 +++- .../RootFeature/Sources/RootView.swift | 9 +++++-- .../Sources/SplashComponent.swift | 1 + .../SplashFeature/Sources/SplashView.swift | 12 ++++++---- 7 files changed, 49 insertions(+), 8 deletions(-) diff --git a/Plugin/DependencyPlugin/ProjectDescriptionHelpers/ModulePaths.swift b/Plugin/DependencyPlugin/ProjectDescriptionHelpers/ModulePaths.swift index a4ff44f..ba6d75f 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 SigninFeature case SplashFeature case RootFeature case BaseFeature diff --git a/Projects/App/Sources/Application/DI/AppComponent.swift b/Projects/App/Sources/Application/DI/AppComponent.swift index bd5f0b3..20109cb 100644 --- a/Projects/App/Sources/Application/DI/AppComponent.swift +++ b/Projects/App/Sources/Application/DI/AppComponent.swift @@ -4,6 +4,8 @@ import RootFeature import RootFeatureInterface import SplashFeature import SplashFeatureInterface +import SigninFeature +import SigninFeatureInterface public final class AppComponent: BootstrapComponent { // private let _keychain: any Keychain @@ -31,4 +33,7 @@ public extension AppComponent { var splashFactory: any SplashFactory { SplashComponent(parent: self) } + var signinFactory: any SigninFactory { + SigninComponent(parent: self) + } } diff --git a/Projects/App/Sources/Application/NeedleGenerated.swift b/Projects/App/Sources/Application/NeedleGenerated.swift index 147b513..d1dac6a 100644 --- a/Projects/App/Sources/Application/NeedleGenerated.swift +++ b/Projects/App/Sources/Application/NeedleGenerated.swift @@ -3,6 +3,8 @@ import NeedleFoundation import RootFeature import RootFeatureInterface +import SigninFeature +import SigninFeatureInterface import SplashFeature import SplashFeatureInterface import SwiftUI @@ -35,6 +37,9 @@ private class RootDependency3944cc797a4a88956fb5Provider: RootDependency { var splashFactory: any SplashFactory { return appComponent.splashFactory } + var signinFactory: any SigninFactory { + return appComponent.signinFactory + } private let appComponent: AppComponent init(appComponent: AppComponent) { self.appComponent = appComponent @@ -44,12 +49,24 @@ private class RootDependency3944cc797a4a88956fb5Provider: RootDependency { private func factory264bfc4d4cb6b0629b40f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { return RootDependency3944cc797a4a88956fb5Provider(appComponent: parent1(component) as! AppComponent) } +private class SigninDependencyde06a9d0b22764487733Provider: SigninDependency { + + + init() { + + } +} +/// ^->AppComponent->SigninComponent +private func factory2882a056d84a613debcce3b0c44298fc1c149afb(_ component: NeedleFoundation.Scope) -> AnyObject { + return SigninDependencyde06a9d0b22764487733Provider() +} #else extension AppComponent: Registration { public func registerItems() { localTable["splashFactory-any SplashFactory"] = { [unowned self] in self.splashFactory as Any } + localTable["signinFactory-any SigninFactory"] = { [unowned self] in self.signinFactory as Any } } } extension SplashComponent: Registration { @@ -60,6 +77,12 @@ extension SplashComponent: Registration { extension RootComponent: Registration { public func registerItems() { keyPathToName[\RootDependency.splashFactory] = "splashFactory-any SplashFactory" + keyPathToName[\RootDependency.signinFactory] = "signinFactory-any SigninFactory" + } +} +extension SigninComponent: Registration { + public func registerItems() { + } } @@ -81,6 +104,7 @@ private func registerProviderFactory(_ componentPath: String, _ factory: @escapi registerProviderFactory("^->AppComponent", factoryEmptyDependencyProvider) registerProviderFactory("^->AppComponent->SplashComponent", factoryace9f05f51d68f4c0677e3b0c44298fc1c149afb) registerProviderFactory("^->AppComponent->RootComponent", factory264bfc4d4cb6b0629b40f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->SigninComponent", factory2882a056d84a613debcce3b0c44298fc1c149afb) } #endif diff --git a/Projects/Feature/RootFeature/Sources/RootComponent.swift b/Projects/Feature/RootFeature/Sources/RootComponent.swift index cfc1397..2fadc6b 100644 --- a/Projects/Feature/RootFeature/Sources/RootComponent.swift +++ b/Projects/Feature/RootFeature/Sources/RootComponent.swift @@ -2,15 +2,18 @@ import RootFeatureInterface import NeedleFoundation import SwiftUI import SplashFeatureInterface +import SigninFeatureInterface public protocol RootDependency: Dependency { var splashFactory: any SplashFactory { get } + var signinFactory: any SigninFactory { get } } public final class RootComponent: Component, RootFactory { public func makeView() -> some View { RootView( - splashFactory: dependency.splashFactory + splashFactory: dependency.splashFactory, + signinFactory: dependency.signinFactory ) } } diff --git a/Projects/Feature/RootFeature/Sources/RootView.swift b/Projects/Feature/RootFeature/Sources/RootView.swift index aec5da4..0b3506c 100644 --- a/Projects/Feature/RootFeature/Sources/RootView.swift +++ b/Projects/Feature/RootFeature/Sources/RootView.swift @@ -2,23 +2,27 @@ import BaseFeature import SwiftUI import DesignSystem import SplashFeatureInterface +import SigninFeatureInterface import ViewUtil struct RootView: View { @EnvironmentObject var appState: AppState private let splashFactory: any SplashFactory + private let signinFactory: any SigninFactory public init( - splashFactory: any SplashFactory + splashFactory: any SplashFactory, + signinFactory: any SigninFactory ) { self.splashFactory = splashFactory + self.signinFactory = signinFactory } var body: some View { ZStack { switch appState.sceneFlow { case .auth: - EmptyView() + signinFactory.makeView().eraseToAnyView() .environmentObject(appState) case .main: @@ -30,6 +34,7 @@ struct RootView: View { .environmentObject(appState) } } + .background(Color.System.background.ignoresSafeArea()) .animation(.easeInOut, value: appState.sceneFlow) .transition(.opacity.animation(.easeInOut)) } diff --git a/Projects/Feature/SplashFeature/Sources/SplashComponent.swift b/Projects/Feature/SplashFeature/Sources/SplashComponent.swift index 3f5d31e..9d0fb46 100644 --- a/Projects/Feature/SplashFeature/Sources/SplashComponent.swift +++ b/Projects/Feature/SplashFeature/Sources/SplashComponent.swift @@ -1,6 +1,7 @@ import SwiftUI import NeedleFoundation import SplashFeatureInterface + public protocol SplashDependency: Dependency { } diff --git a/Projects/Feature/SplashFeature/Sources/SplashView.swift b/Projects/Feature/SplashFeature/Sources/SplashView.swift index a321093..eabbef6 100644 --- a/Projects/Feature/SplashFeature/Sources/SplashView.swift +++ b/Projects/Feature/SplashFeature/Sources/SplashView.swift @@ -20,11 +20,13 @@ struct SplashView: View { .frame(maxWidth: .infinity, maxHeight: /*@START_MENU_TOKEN@*/.infinity/*@END_MENU_TOKEN@*/) .background(Color.System.background.ignoresSafeArea()) .onAppear { - // viewModel.onAppear { - // appState.sceneFlow = .main - // } onError: { _ in - // appState.sceneFlow = .auth - // } +// viewModel.onAppear { +// appState.sceneFlow = .main +// } onError: { _ in + DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { + appState.sceneFlow = .auth + } +// } } } } From b9a9d8a4291b411a418bf2096d94469a661e9e2a Mon Sep 17 00:00:00 2001 From: HongSJae Date: Tue, 23 Apr 2024 20:28:58 +0900 Subject: [PATCH 09/10] =?UTF-8?q?=E2=9C=A8=20::=20Signin=20Publishing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Interface/SigninFactory.swift | 6 ++ Projects/Feature/SigninFeature/Project.swift | 17 +++++ .../Sources/SigninComponent.swift | 14 ++++ .../SigninFeature/Sources/SigninView.swift | 66 +++++++++++++++++++ .../Sources/SigninViewModel.swift | 11 ++++ .../Tests/SigninFeatureTest.swift | 11 ++++ 6 files changed, 125 insertions(+) create mode 100644 Projects/Feature/SigninFeature/Interface/SigninFactory.swift create mode 100644 Projects/Feature/SigninFeature/Project.swift create mode 100644 Projects/Feature/SigninFeature/Sources/SigninComponent.swift create mode 100644 Projects/Feature/SigninFeature/Sources/SigninView.swift create mode 100644 Projects/Feature/SigninFeature/Sources/SigninViewModel.swift create mode 100644 Projects/Feature/SigninFeature/Tests/SigninFeatureTest.swift diff --git a/Projects/Feature/SigninFeature/Interface/SigninFactory.swift b/Projects/Feature/SigninFeature/Interface/SigninFactory.swift new file mode 100644 index 0000000..b470b2f --- /dev/null +++ b/Projects/Feature/SigninFeature/Interface/SigninFactory.swift @@ -0,0 +1,6 @@ +import SwiftUI + +public protocol SigninFactory { + associatedtype SomeView: View + func makeView() -> SomeView +} diff --git a/Projects/Feature/SigninFeature/Project.swift b/Projects/Feature/SigninFeature/Project.swift new file mode 100644 index 0000000..ffa379d --- /dev/null +++ b/Projects/Feature/SigninFeature/Project.swift @@ -0,0 +1,17 @@ +import DependencyPlugin +import ProjectDescription +import ProjectDescriptionHelpers + +let project = Project.module( + name: ModulePaths.Feature.SigninFeature.rawValue, + targets: [ + .interface(module: .feature(.SigninFeature)), + .implements(module: .feature(.SigninFeature), dependencies: [ + .feature(target: .SigninFeature, type: .interface), + .feature(target: .BaseFeature) + ]), + .tests(module: .feature(.SigninFeature), dependencies: [ + .feature(target: .SigninFeature) + ]) + ] +) diff --git a/Projects/Feature/SigninFeature/Sources/SigninComponent.swift b/Projects/Feature/SigninFeature/Sources/SigninComponent.swift new file mode 100644 index 0000000..513c831 --- /dev/null +++ b/Projects/Feature/SigninFeature/Sources/SigninComponent.swift @@ -0,0 +1,14 @@ +import SwiftUI +import NeedleFoundation +import SigninFeatureInterface + +public protocol SigninDependency: Dependency { +} + +public final class SigninComponent: Component, SigninFactory { + public func makeView() -> some View { + SigninView( + viewModel: .init() + ) + } +} diff --git a/Projects/Feature/SigninFeature/Sources/SigninView.swift b/Projects/Feature/SigninFeature/Sources/SigninView.swift new file mode 100644 index 0000000..852de77 --- /dev/null +++ b/Projects/Feature/SigninFeature/Sources/SigninView.swift @@ -0,0 +1,66 @@ +import DesignSystem +import SwiftUI +import BaseFeature + +struct SigninView: View { + private enum FocusField { + case email + case password + } + @FocusState private var focusField: FocusField? + @StateObject var viewModel: SigninViewModel + + init( + viewModel: SigninViewModel + ) { + _viewModel = StateObject(wrappedValue: viewModel) + } + + var body: some View { + VStack(spacing: 49) { + KGImage(.logo) + .frame(width: 254, height: 53) + .padding(.top, 113) + + VStack(spacing: 0) { + KGTextField( + "이메일을 입력해주세요", + text: $viewModel.email, + isError: viewModel.isErrorOccurred, + errorMessage: viewModel.errorMessage + ) { + self.focusField = .password + } + .focused($focusField, equals: .email) + + KGTextField( + "비밀번호를 입력해주세요", + text: $viewModel.password, + isError: viewModel.isErrorOccurred, + errorMessage: viewModel.errorMessage + ) { + viewModel.signinButtonDidTap() + } + .focused($focusField, equals: .password) + + KGButton( + text: "로그인", + action: viewModel.signinButtonDidTap + ) + + HStack(spacing: 16) { + Text("회원가입") + .kgFont(.label, weight: .regular, color: .Grays.gray700) + + Color.Grays.gray700 + .frame(width: 1, height: 16) + + Text("비밀번호 찾기") + .kgFont(.label, weight: .regular, color: .Grays.gray700) + } + } + + Spacer() + } + } +} diff --git a/Projects/Feature/SigninFeature/Sources/SigninViewModel.swift b/Projects/Feature/SigninFeature/Sources/SigninViewModel.swift new file mode 100644 index 0000000..2afb062 --- /dev/null +++ b/Projects/Feature/SigninFeature/Sources/SigninViewModel.swift @@ -0,0 +1,11 @@ +import BaseFeature +import Combine + +final class SigninViewModel: BaseViewModel { + @Published var email: String = "" + @Published var password: String = "" + + func signinButtonDidTap() { + print("Signin") + } +} diff --git a/Projects/Feature/SigninFeature/Tests/SigninFeatureTest.swift b/Projects/Feature/SigninFeature/Tests/SigninFeatureTest.swift new file mode 100644 index 0000000..6223252 --- /dev/null +++ b/Projects/Feature/SigninFeature/Tests/SigninFeatureTest.swift @@ -0,0 +1,11 @@ +import XCTest + +final class SigninFeatureTests: XCTestCase { + override func setUpWithError() throws {} + + override func tearDownWithError() throws {} + + func testExample() { + XCTAssertEqual(1, 1) + } +} From ba680a87e57f26e055bb5ab3d05a9932ffa2e671 Mon Sep 17 00:00:00 2001 From: HongSJae Date: Tue, 23 Apr 2024 20:29:13 +0900 Subject: [PATCH 10/10] =?UTF-8?q?=E2=9C=A8=20::=20DesignSystem=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pressed Color 추가 --- .../Sources/Button/KGButtonStyle.swift | 61 +++++++++++++------ .../Sources/TextField/KGTextField.swift | 1 - 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/Projects/UserInterface/DesignSystem/Sources/Button/KGButtonStyle.swift b/Projects/UserInterface/DesignSystem/Sources/Button/KGButtonStyle.swift index 841172b..9a1b591 100644 --- a/Projects/UserInterface/DesignSystem/Sources/Button/KGButtonStyle.swift +++ b/Projects/UserInterface/DesignSystem/Sources/Button/KGButtonStyle.swift @@ -11,24 +11,6 @@ public struct KGButtonStyle: ButtonStyle { public enum Style { case green case black - - var background: Color { - switch self { - case .green: - Color.Greens.main - case .black: - Color.Grays.gray1000 - } - } - - var foreground: Color { - switch self { - case .green: - Color.Greens.secondary - case .black: - Color.Grays.gray700 - } - } } let style: Style @@ -49,11 +31,50 @@ extension KGButtonStyle { var body: some View { configuration.label - .kgFont(.m3, weight: .semiBold, color: style.foreground) + .kgFont( + .m3, + weight: .semiBold, + color: configuration.isPressed ? style.pressedForeground : style.foreground + ) .padding(.vertical, 16) .frame(maxWidth: .infinity) - .background(style.background) + .background(configuration.isPressed ? style.pressedBackground : style.background) .clipCornerRadius(isEditing ? 0 : 8) } } } + +// Color Setting +private extension KGButtonStyle.Style { + var background: Color { + switch self { + case .green: + Color.Greens.main + case .black: + Color.Grays.gray1000 + } + } + + var foreground: Color { + switch self { + case .green: + Color.Greens.secondary + case .black: + Color.Grays.gray700 + } + } + + var pressedBackground: Color { + switch self { + default: + Color.Greens.secondary + } + } + + var pressedForeground: Color { + switch self { + default: + Color.Grays.gray100 + } + } +} diff --git a/Projects/UserInterface/DesignSystem/Sources/TextField/KGTextField.swift b/Projects/UserInterface/DesignSystem/Sources/TextField/KGTextField.swift index 5f7dce7..afcdcb3 100644 --- a/Projects/UserInterface/DesignSystem/Sources/TextField/KGTextField.swift +++ b/Projects/UserInterface/DesignSystem/Sources/TextField/KGTextField.swift @@ -67,7 +67,6 @@ public struct KGTextField: View { } .padding(.vertical, 8) .padding(.horizontal, 24) - .background(.gray) .animation(.easeIn(duration: 0.2), value: isErrorAndNotEmpty) .animation(.easeIn(duration: 0.2), value: isFocused) }