From c90d8bbb0765627633db74fc0c347543e4b30fe7 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 15 Jul 2025 15:37:52 +0900 Subject: [PATCH 01/56] =?UTF-8?q?feat/#159:=20fastlane=20match=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/fastlane/Matchfile | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 Poppool/fastlane/Matchfile diff --git a/Poppool/fastlane/Matchfile b/Poppool/fastlane/Matchfile new file mode 100644 index 00000000..3d9575a3 --- /dev/null +++ b/Poppool/fastlane/Matchfile @@ -0,0 +1,8 @@ +git_url("git@github.com:PopPool/iOS-Signing.git") + +storage_mode("git") + +type("development") # The default type, can be: appstore, adhoc, enterprise or development + +app_identifier(["com.poppoolIOS.poppool"]) +username("thddudgns972@gmail.com") \ No newline at end of file From dc0e3f6e00ea1b0fab0047af45550aa1a89c30ce Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 15 Jul 2025 16:42:20 +0900 Subject: [PATCH 02/56] =?UTF-8?q?feat/#159:=20=EB=B2=88=EB=93=A4=20ID=20?= =?UTF-8?q?=EC=99=80=EC=9D=BC=EB=93=9C=EC=B9=B4=EB=93=9C=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 --- Poppool/fastlane/Matchfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Poppool/fastlane/Matchfile b/Poppool/fastlane/Matchfile index 3d9575a3..dff1856f 100644 --- a/Poppool/fastlane/Matchfile +++ b/Poppool/fastlane/Matchfile @@ -4,5 +4,5 @@ storage_mode("git") type("development") # The default type, can be: appstore, adhoc, enterprise or development -app_identifier(["com.poppoolIOS.poppool"]) +app_identifier(["com.poppoolIOS.poppool", "com.poppoolIOS.*"]) username("thddudgns972@gmail.com") \ No newline at end of file From cd7903fb133c69f880d6a7bee6710248f5f28954 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 15 Jul 2025 16:45:26 +0900 Subject: [PATCH 03/56] =?UTF-8?q?chore/#159:=20Signing=20=EC=84=B8?= =?UTF-8?q?=ED=8C=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/Poppool.xcodeproj/project.pbxproj | 10 +++++----- .../SearchFeature.xcodeproj/project.pbxproj | 11 ++++++++--- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index c4430902..7c50bd60 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -424,11 +424,11 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Poppool/Resource/Poppool.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 9JZYRP3D46; ENABLE_USER_SCRIPT_SANDBOXING = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Poppool/Resource/Info.plist; @@ -450,7 +450,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = PoppoolGitHubAction; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.poppoolIOS.poppool"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -475,7 +475,7 @@ CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 9JZYRP3D46; ENABLE_USER_SCRIPT_SANDBOXING = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Poppool/Resource/Info.plist; @@ -497,7 +497,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = PoppoolGitHubAction; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.poppoolIOS.poppool"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj index 271b78b4..3575ab6f 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj @@ -678,7 +678,7 @@ CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 9JZYRP3D46; GENERATE_INFOPLIST_FILE = YES; IBSC_COMPILER_AUTO_ACTIVATE_CUSTOM_FONTS = YES; INFOPLIST_FILE = SearchFeatureDemo/Resource/Info.plist; @@ -695,7 +695,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.SearchFeatureDemo; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = poppoolSearchFeatureDemoProvisioning; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.poppoolIOS.*"; SKIP_INSTALL = NO; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; @@ -714,8 +714,11 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_SKIP_APP_STORE_DEPLOYMENT = NO; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 9JZYRP3D46; GENERATE_INFOPLIST_FILE = YES; IBSC_COMPILER_AUTO_ACTIVATE_CUSTOM_FONTS = YES; INFOPLIST_FILE = SearchFeatureDemo/Resource/Info.plist; @@ -731,6 +734,8 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.SearchFeatureDemo; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.poppoolIOS.*"; SKIP_INSTALL = NO; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; From 98252317bf7e854421f784d4f2661e090fd90819 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 15 Jul 2025 19:09:49 +0900 Subject: [PATCH 04/56] =?UTF-8?q?feat/#159:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=ED=94=8C=EB=9D=BC=EC=9D=B4=ED=8A=B8=20=EC=97=85=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/Gemfile | 3 +++ Poppool/Poppool.xcodeproj/project.pbxproj | 8 +++--- Poppool/Poppool/Resource/Info.plist | 2 +- Poppool/fastlane/Appfile | 8 ++++++ Poppool/fastlane/Fastfile | 30 +++++++++++++++++++++++ 5 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 Poppool/Gemfile create mode 100644 Poppool/fastlane/Appfile create mode 100644 Poppool/fastlane/Fastfile diff --git a/Poppool/Gemfile b/Poppool/Gemfile new file mode 100644 index 00000000..7a118b49 --- /dev/null +++ b/Poppool/Gemfile @@ -0,0 +1,3 @@ +source "https://rubygems.org" + +gem "fastlane" diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index 7c50bd60..37c8e70f 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -426,7 +426,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 250715.1858; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 9JZYRP3D46; ENABLE_USER_SCRIPT_SANDBOXING = NO; @@ -446,7 +446,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.0; + MARKETING_VERSION = 1.1.2; PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -473,7 +473,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 250715.1858; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 9JZYRP3D46; ENABLE_USER_SCRIPT_SANDBOXING = NO; @@ -493,7 +493,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.0; + MARKETING_VERSION = 1.1.2; PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/Poppool/Poppool/Resource/Info.plist b/Poppool/Poppool/Resource/Info.plist index 3f04e5cc..3ad9f07b 100644 --- a/Poppool/Poppool/Resource/Info.plist +++ b/Poppool/Poppool/Resource/Info.plist @@ -18,7 +18,7 @@ CFBundleVersion - $(CURRENT_PROJECT_VERSION) + 250715.1858 KAKAO_AUTH_APP_KEY ${KAKAO_AUTH_APP_KEY} LSApplicationQueriesSchemes diff --git a/Poppool/fastlane/Appfile b/Poppool/fastlane/Appfile new file mode 100644 index 00000000..0a657e6a --- /dev/null +++ b/Poppool/fastlane/Appfile @@ -0,0 +1,8 @@ +app_identifier("com.poppoolIOS.poppool") # The bundle identifier of your app +apple_id("thddudgns972@gmail.com") # Your Apple Developer Portal username + +itc_team_id("127871563") # App Store Connect Team ID +team_id("9JZYRP3D46") # Developer Portal Team ID + +# For more information about the Appfile, see: +# https://docs.fastlane.tools/advanced/#appfile diff --git a/Poppool/fastlane/Fastfile b/Poppool/fastlane/Fastfile new file mode 100644 index 00000000..32296fe6 --- /dev/null +++ b/Poppool/fastlane/Fastfile @@ -0,0 +1,30 @@ +# This file contains the fastlane.tools configuration +# You can find the documentation at https://docs.fastlane.tools +# +# For a list of all available actions, check out +# +# https://docs.fastlane.tools/actions +# +# For a list of all available plugins, check out +# +# https://docs.fastlane.tools/plugins/available-plugins +# + +# Uncomment the line if you want fastlane to automatically update itself +# update_fastlane + +default_platform(:ios) + +platform :ios do + desc "Push a new beta build to TestFlight" + lane :beta do + match(type: "appstore") + timestamp = Time.now.strftime("%y%m%d.%H%M") + increment_build_number( + build_number: timestamp, + xcodeproj: "Poppool.xcodeproj" + ) + build_app(workspace: "Poppool.xcworkspace", scheme: "Poppool") + upload_to_testflight + end +end From 05608fd35ed450212006fcd37199aeb138cd53ff Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 15 Jul 2025 20:40:46 +0900 Subject: [PATCH 05/56] =?UTF-8?q?chore/#159:=20=ED=99=98=EA=B2=BD=EB=B3=80?= =?UTF-8?q?=EC=88=98=20=ED=8C=8C=EC=9D=BC=20ingnore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 01fa263e..0f7677da 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,4 @@ fastlane/test_output # Cursor **/buildServer.json .vscode/* +*.env From 35fb97b6252e8b4a42da46345492bd77d09c5e6e Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Jul 2025 14:00:12 +0900 Subject: [PATCH 06/56] =?UTF-8?q?chore/#160:=20=EC=95=B1=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC=EC=9A=A9=20=ED=94=84=EB=A1=9C=EB=B9=84=EC=A0=80?= =?UTF-8?q?=EB=8B=9D=20=EC=84=A4=EC=A0=95=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/Poppool.xcodeproj/project.pbxproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index c4430902..925b6c8e 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -212,7 +212,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1540; + LastSwiftUpdateCheck = 1640; LastUpgradeCheck = 1640; TargetAttributes = { BDCA41BC2CF35AC0005EECF6 = { @@ -424,11 +424,11 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Poppool/Resource/Poppool.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 9JZYRP3D46; ENABLE_USER_SCRIPT_SANDBOXING = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Poppool/Resource/Info.plist; @@ -450,7 +450,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = PoppoolGitHubAction; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.poppoolIOS.poppool"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -475,7 +475,7 @@ CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 9JZYRP3D46; ENABLE_USER_SCRIPT_SANDBOXING = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Poppool/Resource/Info.plist; @@ -497,7 +497,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = PoppoolGitHubAction; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.poppoolIOS.poppool"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; From 925d2e43190808cf35222fe1bacdfd5056603569 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Jul 2025 14:00:57 +0900 Subject: [PATCH 07/56] =?UTF-8?q?chore/#160:=20gitignore=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 모든 env 파일 무시 규칙 추가 --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 01fa263e..0f7677da 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,4 @@ fastlane/test_output # Cursor **/buildServer.json .vscode/* +*.env From 2c79e234dd98e92823926111a497a0e3cf28776a Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Jul 2025 14:01:27 +0900 Subject: [PATCH 08/56] =?UTF-8?q?feat/#160:=20LoginFeature=20=EB=AA=A8?= =?UTF-8?q?=EB=93=88=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EA=B8=B0=EB=B3=B8?= =?UTF-8?q?=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoginFeature.xcodeproj/project.pbxproj | 803 ++++++++++++++++++ .../xcschemes/LoginFeatureDemo.xcscheme | 78 ++ .../LoginFeature/Factory/LoginFactory.swift | 13 + .../LoginFeature/Login/LoginReactor.swift | 36 + .../LoginFeature/Login/LoginView.swift | 30 + .../Login/LoginViewController.swift | 49 ++ .../LoginFeatureDemo/App/AppDelegate.swift | 42 + .../LoginFeatureDemo/App/SceneDelegate.swift | 28 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 35 + .../Resource/Assets.xcassets/Contents.json | 6 + .../Base.lproj/LaunchScreen.storyboard | 25 + .../LoginFeatureDemo/Resource/Info.plist | 23 + .../Source/Factory/LoginFactory.swift | 7 + .../SearchFeature.xcodeproj/project.pbxproj | 11 +- 15 files changed, 1194 insertions(+), 3 deletions(-) create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/project.pbxproj create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/xcshareddata/xcschemes/LoginFeatureDemo.xcscheme create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/LoginFactory.swift create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginView.swift create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/AppDelegate.swift create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/SceneDelegate.swift create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/Contents.json create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Base.lproj/LaunchScreen.storyboard create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Info.plist create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeatureInterface/Source/Factory/LoginFactory.swift diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/project.pbxproj new file mode 100644 index 00000000..66ca0953 --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/project.pbxproj @@ -0,0 +1,803 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + 05A7C1AF2E2679290010F1CD /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7C1AE2E2679290010F1CD /* DesignSystem.framework */; }; + 05A7C1B22E26799A0010F1CD /* LoginFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7C15F2E26768A0010F1CD /* LoginFeature.framework */; }; + 05A7C1B32E26799A0010F1CD /* LoginFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7C15F2E26768A0010F1CD /* LoginFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05A7C1B62E26799A0010F1CD /* LoginFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7C16E2E2676E60010F1CD /* LoginFeatureInterface.framework */; }; + 05A7C1B72E26799A0010F1CD /* LoginFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7C16E2E2676E60010F1CD /* LoginFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05A7C1BC2E2679FC0010F1CD /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7C1BB2E2679FC0010F1CD /* Infrastructure.framework */; }; + 05A7C1BD2E2679FC0010F1CD /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7C1BB2E2679FC0010F1CD /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05A7C1BE2E267A120010F1CD /* LoginFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7C16E2E2676E60010F1CD /* LoginFeatureInterface.framework */; }; + 05A7C1D32E267AF80010F1CD /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7C1D22E267AF80010F1CD /* ReactorKit */; }; + 05A7C1D62E267B130010F1CD /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7C1D52E267B130010F1CD /* RxCocoa */; }; + 05A7C1D82E267B130010F1CD /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7C1D72E267B130010F1CD /* RxSwift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 05A7C1B42E26799A0010F1CD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 05A7C1562E26768A0010F1CD /* Project object */; + proxyType = 1; + remoteGlobalIDString = 05A7C15E2E26768A0010F1CD; + remoteInfo = LoginFeature; + }; + 05A7C1B82E26799A0010F1CD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 05A7C1562E26768A0010F1CD /* Project object */; + proxyType = 1; + remoteGlobalIDString = 05A7C16D2E2676E60010F1CD; + remoteInfo = LoginFeatureInterface; + }; + 05A7C1C02E267A120010F1CD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 05A7C1562E26768A0010F1CD /* Project object */; + proxyType = 1; + remoteGlobalIDString = 05A7C16D2E2676E60010F1CD; + remoteInfo = LoginFeatureInterface; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 05A7C1BA2E26799A0010F1CD /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 05A7C1BD2E2679FC0010F1CD /* Infrastructure.framework in Embed Frameworks */, + 05A7C1B32E26799A0010F1CD /* LoginFeature.framework in Embed Frameworks */, + 05A7C1B72E26799A0010F1CD /* LoginFeatureInterface.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 05A7C15F2E26768A0010F1CD /* LoginFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LoginFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05A7C16E2E2676E60010F1CD /* LoginFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LoginFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05A7C1922E26771A0010F1CD /* LoginFeatureDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LoginFeatureDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 05A7C1AE2E2679290010F1CD /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05A7C1BB2E2679FC0010F1CD /* Infrastructure.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Infrastructure.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 05A7C1A32E26771B0010F1CD /* Exceptions for "LoginFeatureDemo" folder in "LoginFeatureDemo" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Resource/Info.plist, + ); + target = 05A7C1912E26771A0010F1CD /* LoginFeatureDemo */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 05A7C1612E26768A0010F1CD /* LoginFeature */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = LoginFeature; + sourceTree = ""; + }; + 05A7C16F2E2676E60010F1CD /* LoginFeatureInterface */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = LoginFeatureInterface; + sourceTree = ""; + }; + 05A7C1932E26771A0010F1CD /* LoginFeatureDemo */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + 05A7C1A32E26771B0010F1CD /* Exceptions for "LoginFeatureDemo" folder in "LoginFeatureDemo" target */, + ); + path = LoginFeatureDemo; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 05A7C15C2E26768A0010F1CD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 05A7C1D62E267B130010F1CD /* RxCocoa in Frameworks */, + 05A7C1D32E267AF80010F1CD /* ReactorKit in Frameworks */, + 05A7C1D82E267B130010F1CD /* RxSwift in Frameworks */, + 05A7C1BE2E267A120010F1CD /* LoginFeatureInterface.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 05A7C16B2E2676E60010F1CD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 05A7C1AF2E2679290010F1CD /* DesignSystem.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 05A7C18F2E26771A0010F1CD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 05A7C1BC2E2679FC0010F1CD /* Infrastructure.framework in Frameworks */, + 05A7C1B22E26799A0010F1CD /* LoginFeature.framework in Frameworks */, + 05A7C1B62E26799A0010F1CD /* LoginFeatureInterface.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 05A7C1552E26768A0010F1CD = { + isa = PBXGroup; + children = ( + 05A7C1612E26768A0010F1CD /* LoginFeature */, + 05A7C16F2E2676E60010F1CD /* LoginFeatureInterface */, + 05A7C1932E26771A0010F1CD /* LoginFeatureDemo */, + 05A7C1AD2E2679290010F1CD /* Frameworks */, + 05A7C1602E26768A0010F1CD /* Products */, + ); + sourceTree = ""; + }; + 05A7C1602E26768A0010F1CD /* Products */ = { + isa = PBXGroup; + children = ( + 05A7C15F2E26768A0010F1CD /* LoginFeature.framework */, + 05A7C16E2E2676E60010F1CD /* LoginFeatureInterface.framework */, + 05A7C1922E26771A0010F1CD /* LoginFeatureDemo.app */, + ); + name = Products; + sourceTree = ""; + }; + 05A7C1AD2E2679290010F1CD /* Frameworks */ = { + isa = PBXGroup; + children = ( + 05A7C1BB2E2679FC0010F1CD /* Infrastructure.framework */, + 05A7C1AE2E2679290010F1CD /* DesignSystem.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 05A7C15A2E26768A0010F1CD /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 05A7C1692E2676E60010F1CD /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 05A7C15E2E26768A0010F1CD /* LoginFeature */ = { + isa = PBXNativeTarget; + buildConfigurationList = 05A7C1662E26768A0010F1CD /* Build configuration list for PBXNativeTarget "LoginFeature" */; + buildPhases = ( + 05A7C15A2E26768A0010F1CD /* Headers */, + 05A7C15B2E26768A0010F1CD /* Sources */, + 05A7C15C2E26768A0010F1CD /* Frameworks */, + 05A7C15D2E26768A0010F1CD /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 05A7C1C12E267A120010F1CD /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + 05A7C1612E26768A0010F1CD /* LoginFeature */, + ); + name = LoginFeature; + packageProductDependencies = ( + 05A7C1D22E267AF80010F1CD /* ReactorKit */, + 05A7C1D52E267B130010F1CD /* RxCocoa */, + 05A7C1D72E267B130010F1CD /* RxSwift */, + ); + productName = LoginFeature; + productReference = 05A7C15F2E26768A0010F1CD /* LoginFeature.framework */; + productType = "com.apple.product-type.framework"; + }; + 05A7C16D2E2676E60010F1CD /* LoginFeatureInterface */ = { + isa = PBXNativeTarget; + buildConfigurationList = 05A7C1722E2676E60010F1CD /* Build configuration list for PBXNativeTarget "LoginFeatureInterface" */; + buildPhases = ( + 05A7C1692E2676E60010F1CD /* Headers */, + 05A7C16A2E2676E60010F1CD /* Sources */, + 05A7C16B2E2676E60010F1CD /* Frameworks */, + 05A7C16C2E2676E60010F1CD /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 05A7C16F2E2676E60010F1CD /* LoginFeatureInterface */, + ); + name = LoginFeatureInterface; + packageProductDependencies = ( + ); + productName = LoginFeatureInterface; + productReference = 05A7C16E2E2676E60010F1CD /* LoginFeatureInterface.framework */; + productType = "com.apple.product-type.framework"; + }; + 05A7C1912E26771A0010F1CD /* LoginFeatureDemo */ = { + isa = PBXNativeTarget; + buildConfigurationList = 05A7C1A42E26771B0010F1CD /* Build configuration list for PBXNativeTarget "LoginFeatureDemo" */; + buildPhases = ( + 05A7C18E2E26771A0010F1CD /* Sources */, + 05A7C18F2E26771A0010F1CD /* Frameworks */, + 05A7C1902E26771A0010F1CD /* Resources */, + 05A7C1BA2E26799A0010F1CD /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 05A7C1B52E26799A0010F1CD /* PBXTargetDependency */, + 05A7C1B92E26799A0010F1CD /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + 05A7C1932E26771A0010F1CD /* LoginFeatureDemo */, + ); + name = LoginFeatureDemo; + packageProductDependencies = ( + ); + productName = LoginFeatureDemo; + productReference = 05A7C1922E26771A0010F1CD /* LoginFeatureDemo.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 05A7C1562E26768A0010F1CD /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1640; + LastUpgradeCheck = 1640; + TargetAttributes = { + 05A7C15E2E26768A0010F1CD = { + CreatedOnToolsVersion = 16.4; + }; + 05A7C16D2E2676E60010F1CD = { + CreatedOnToolsVersion = 16.4; + }; + 05A7C1912E26771A0010F1CD = { + CreatedOnToolsVersion = 16.4; + }; + }; + }; + buildConfigurationList = 05A7C1592E26768A0010F1CD /* Build configuration list for PBXProject "LoginFeature" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 05A7C1552E26768A0010F1CD; + minimizedProjectReferenceProxies = 1; + packageReferences = ( + 05A7C1D12E267AF80010F1CD /* XCRemoteSwiftPackageReference "ReactorKit" */, + 05A7C1D42E267B130010F1CD /* XCRemoteSwiftPackageReference "RxSwift" */, + ); + preferredProjectObjectVersion = 77; + productRefGroup = 05A7C1602E26768A0010F1CD /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 05A7C15E2E26768A0010F1CD /* LoginFeature */, + 05A7C16D2E2676E60010F1CD /* LoginFeatureInterface */, + 05A7C1912E26771A0010F1CD /* LoginFeatureDemo */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 05A7C15D2E26768A0010F1CD /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 05A7C16C2E2676E60010F1CD /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 05A7C1902E26771A0010F1CD /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 05A7C15B2E26768A0010F1CD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 05A7C16A2E2676E60010F1CD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 05A7C18E2E26771A0010F1CD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 05A7C1B52E26799A0010F1CD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 05A7C15E2E26768A0010F1CD /* LoginFeature */; + targetProxy = 05A7C1B42E26799A0010F1CD /* PBXContainerItemProxy */; + }; + 05A7C1B92E26799A0010F1CD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 05A7C16D2E2676E60010F1CD /* LoginFeatureInterface */; + targetProxy = 05A7C1B82E26799A0010F1CD /* PBXContainerItemProxy */; + }; + 05A7C1C12E267A120010F1CD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 05A7C16D2E2676E60010F1CD /* LoginFeatureInterface */; + targetProxy = 05A7C1C02E267A120010F1CD /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 05A7C1642E26768A0010F1CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.5; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 05A7C1652E26768A0010F1CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.5; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 05A7C1672E26768A0010F1CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.LoginFeature; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_MODULE = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Debug; + }; + 05A7C1682E26768A0010F1CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.LoginFeature; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_MODULE = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Release; + }; + 05A7C1732E2676E60010F1CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.LoginFeatureInterface; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_MODULE = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Debug; + }; + 05A7C1742E2676E60010F1CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.LoginFeatureInterface; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_MODULE = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Release; + }; + 05A7C1A52E26771B0010F1CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 9JZYRP3D46; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = LoginFeatureDemo/Resource/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.LoginFeatureDemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.poppoolIOS.*"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Debug; + }; + 05A7C1A62E26771B0010F1CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 9JZYRP3D46; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = LoginFeatureDemo/Resource/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.LoginFeatureDemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.poppoolIOS.*"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 05A7C1592E26768A0010F1CD /* Build configuration list for PBXProject "LoginFeature" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 05A7C1642E26768A0010F1CD /* Debug */, + 05A7C1652E26768A0010F1CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 05A7C1662E26768A0010F1CD /* Build configuration list for PBXNativeTarget "LoginFeature" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 05A7C1672E26768A0010F1CD /* Debug */, + 05A7C1682E26768A0010F1CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 05A7C1722E2676E60010F1CD /* Build configuration list for PBXNativeTarget "LoginFeatureInterface" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 05A7C1732E2676E60010F1CD /* Debug */, + 05A7C1742E2676E60010F1CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 05A7C1A42E26771B0010F1CD /* Build configuration list for PBXNativeTarget "LoginFeatureDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 05A7C1A52E26771B0010F1CD /* Debug */, + 05A7C1A62E26771B0010F1CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 05A7C1D12E267AF80010F1CD /* XCRemoteSwiftPackageReference "ReactorKit" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/ReactorKit/ReactorKit"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 3.2.0; + }; + }; + 05A7C1D42E267B130010F1CD /* XCRemoteSwiftPackageReference "RxSwift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/ReactiveX/RxSwift"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 6.9.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 05A7C1D22E267AF80010F1CD /* ReactorKit */ = { + isa = XCSwiftPackageProductDependency; + package = 05A7C1D12E267AF80010F1CD /* XCRemoteSwiftPackageReference "ReactorKit" */; + productName = ReactorKit; + }; + 05A7C1D52E267B130010F1CD /* RxCocoa */ = { + isa = XCSwiftPackageProductDependency; + package = 05A7C1D42E267B130010F1CD /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxCocoa; + }; + 05A7C1D72E267B130010F1CD /* RxSwift */ = { + isa = XCSwiftPackageProductDependency; + package = 05A7C1D42E267B130010F1CD /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxSwift; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 05A7C1562E26768A0010F1CD /* Project object */; +} diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/xcshareddata/xcschemes/LoginFeatureDemo.xcscheme b/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/xcshareddata/xcschemes/LoginFeatureDemo.xcscheme new file mode 100644 index 00000000..28ce13b8 --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/xcshareddata/xcschemes/LoginFeatureDemo.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/LoginFactory.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/LoginFactory.swift new file mode 100644 index 00000000..5f381fba --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/LoginFactory.swift @@ -0,0 +1,13 @@ +import UIKit + +import DesignSystem +import LoginFeatureInterface + +public final class LoginFactoryImpl: LoginFactory { + + public init() { } + + public func make() -> BaseViewController { + return LoginViewController() + } +} diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift new file mode 100644 index 00000000..fa329d5e --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift @@ -0,0 +1,36 @@ +import ReactorKit +import RxSwift +import RxCocoa + +final class LoginReactor: Reactor { + + // MARK: - Reactor + enum Action { } + + enum Mutation { } + + struct State { } + + // MARK: - properties + var initialState: State + var disposeBag = DisposeBag() + + // MARK: - init + init() { + self.initialState = State() + } + + // MARK: - Reactor Methods + func mutate(action: Action) -> Observable { + switch action { } + } + + func reduce(state: State, mutation: Mutation) -> State { + var newState = state + + switch mutation { } + + return newState + } +} + diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginView.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginView.swift new file mode 100644 index 00000000..1ca859d3 --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginView.swift @@ -0,0 +1,30 @@ +import UIKit + +import SnapKit + +final class LoginView: UIView { + // MARK: - Properties + + // MARK: - init + init() { + super.init(frame: .zero) + + self.addViews() + self.setupConstraints() + self.configureUI() + } + + required init?(coder: NSCoder) { + fatalError("\(#file), \(#function) Error") + } +} + +// MARK: - SetUp +private extension LoginView { + func addViews() { } + + func setupConstraints() { } + + func configureUI() { } +} + diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift new file mode 100644 index 00000000..4da8d15f --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift @@ -0,0 +1,49 @@ +import UIKit + +import DesignSystem +import SnapKit +import RxCocoa +import RxSwift +import ReactorKit + +final class LoginViewController: BaseViewController, View { + + typealias Reactor = LoginReactor + + // MARK: - Properties + var disposeBag = DisposeBag() + + private var mainView = LoginView() + + override func loadView() { + self.view = mainView + } +} + +// MARK: - Life Cycle +extension LoginViewController { + override func viewDidLoad() { + super.viewDidLoad() + + self.addViews() + self.setupConstraints() + self.configureUI() + } +} + +// MARK: - SetUp +private extension LoginViewController { + func addViews() { } + + func setupConstraints() { } + + func configureUI() { + mainView.backgroundColor = .black + } +} + +extension LoginViewController { + func bind(reactor: Reactor) { } +} + + diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/AppDelegate.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/AppDelegate.swift new file mode 100644 index 00000000..cf86052d --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/AppDelegate.swift @@ -0,0 +1,42 @@ +import UIKit + +import Infrastructure +import LoginFeature +import LoginFeatureInterface + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + + self.registerDependencies() + self.registerFactory() + + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } +} + +extension AppDelegate { + private func registerDependencies() { + // MARK: Register Service + + // MARK: Resolve service + + // MARK: Register repository + + // MARK: Resolve repository + + // MARK: Register UseCase + + } + + private func registerFactory() { + DIContainer.register(LoginFactory.self) { return LoginFactoryImpl() } + } +} diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/SceneDelegate.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/SceneDelegate.swift new file mode 100644 index 00000000..f2890258 --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/SceneDelegate.swift @@ -0,0 +1,28 @@ +import UIKit + +import LoginFeatureInterface +import Infrastructure + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + guard let windowScene = (scene as? UIWindowScene) else { return } + + window = UIWindow(windowScene: windowScene) + + let navigationController = UINavigationController() + @Dependency var loginFactory: LoginFactory + + navigationController.pushViewController( + loginFactory.make(), + animated: false + ) + + window?.rootViewController = navigationController + window?.makeKeyAndVisible() + } +} + diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/AccentColor.colorset/Contents.json b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..23058801 --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/Contents.json b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Base.lproj/LaunchScreen.storyboard b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..865e9329 --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Info.plist b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Info.plist new file mode 100644 index 00000000..0eb786dc --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Info.plist @@ -0,0 +1,23 @@ + + + + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + + + + + + diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeatureInterface/Source/Factory/LoginFactory.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeatureInterface/Source/Factory/LoginFactory.swift new file mode 100644 index 00000000..4ddfe55d --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeatureInterface/Source/Factory/LoginFactory.swift @@ -0,0 +1,7 @@ +import UIKit + +import DesignSystem + +public protocol LoginFactory { + func make() -> BaseViewController +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj index 271b78b4..b2cc794d 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj @@ -678,7 +678,7 @@ CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 9JZYRP3D46; GENERATE_INFOPLIST_FILE = YES; IBSC_COMPILER_AUTO_ACTIVATE_CUSTOM_FONTS = YES; INFOPLIST_FILE = SearchFeatureDemo/Resource/Info.plist; @@ -695,7 +695,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.SearchFeatureDemo; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = poppoolSearchFeatureDemoProvisioning; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.poppoolIOS.*"; SKIP_INSTALL = NO; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; @@ -714,8 +714,11 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_SKIP_APP_STORE_DEPLOYMENT = NO; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 9JZYRP3D46; GENERATE_INFOPLIST_FILE = YES; IBSC_COMPILER_AUTO_ACTIVATE_CUSTOM_FONTS = YES; INFOPLIST_FILE = SearchFeatureDemo/Resource/Info.plist; @@ -731,6 +734,8 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.SearchFeatureDemo; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.poppoolIOS.*"; SKIP_INSTALL = NO; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; From 1f016b59bc36fc3e1de7c233a81199edd18e20b5 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Jul 2025 15:14:15 +0900 Subject: [PATCH 09/56] =?UTF-8?q?feat/#160:=20=EA=B8=B0=EC=A1=B4=EC=9D=98?= =?UTF-8?q?=20LoginScene=20=EB=B3=B5=EC=82=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../contents.xcworkspacedata | 3 + .../LoginFeature/Login/LastLoginView.swift | 209 ++++++++++++++++++ .../Login/Main/LoginController.swift | 88 ++++++++ .../Login/Main/LoginReactor.swift | 148 +++++++++++++ .../LoginFeature/Login/Main/LoginView.swift | 129 +++++++++++ .../Login/Sub/SubLoginController.swift | 88 ++++++++ .../Login/Sub/SubLoginReactor.swift | 144 ++++++++++++ .../LoginFeature/Login/Sub/SubLoginView.swift | 130 +++++++++++ 8 files changed, 939 insertions(+) create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LastLoginView.swift create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Main/LoginController.swift create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Main/LoginReactor.swift create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Main/LoginView.swift create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginController.swift create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginReactor.swift create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginView.swift diff --git a/Poppool/Poppool.xcworkspace/contents.xcworkspacedata b/Poppool/Poppool.xcworkspace/contents.xcworkspacedata index 67537ffe..dc27e306 100644 --- a/Poppool/Poppool.xcworkspace/contents.xcworkspacedata +++ b/Poppool/Poppool.xcworkspace/contents.xcworkspacedata @@ -21,6 +21,9 @@ + + diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LastLoginView.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LastLoginView.swift new file mode 100644 index 00000000..102c5fff --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LastLoginView.swift @@ -0,0 +1,209 @@ +import UIKit + +import SnapKit + +final class LastLoginView: UIView { + + /// 방향에 따라 툴팁을 다르게 표시합니다 + enum TipDirection { + case pointUp + case pointDown + } + + /// 툴팁의 색상을 지정하였습니다 + /// 텍스트 컬러 또한 TipColor에 따라 수정됩니다 + enum TipColor { + case blu500 + case w100 + + var color: UIColor { + switch self { + case .blu500: return UIColor.blu500 + case .w100: return UIColor.w100 + } + } + + var textColor: UIColor { + switch self { + case .blu500: return UIColor.w100 + case .w100: return UIColor.blu500 + } + } + } + + // MARK: - Properties + + private let bgView: UIView = { + return UIView() + }() + + private let notificationLabel: UILabel = { + let label = UILabel() + label.font = .korFont(style: .medium, size: 13) + return label + }() + + private var colorType: TipColor { + didSet { + self.setNeedsDisplay() + } + } + + private var midX: CGFloat { + return self.bounds.midX + } + + private var direction: TipDirection + + // MARK: - init + + /// 툴팁 뷰를 생성합니다 + /// - Parameters: + /// - colorType: 툴팁의 색상(UIColor)을 인자로 받습니다 - w100, blu500 + /// - direction: 툴팁의 방향을 인자로 받습니다 - up / down + init(colorType: TipColor, direction: TipDirection, text: String?) { + self.colorType = colorType + self.direction = direction + super.init(frame: .zero) + setupLayer(color: colorType) + notificationLabel.textColor = colorType.textColor + notificationLabel.text = text + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func draw(_ rect: CGRect) { + drawToolTip() + } +} + +extension LastLoginView { + + // MARK: - Methods + + private func setupLayer(color: TipColor) { + self.backgroundColor = .clear + addSubview(bgView) + + bgView.snp.makeConstraints { make in + make.height.equalTo(45) + make.edges.equalToSuperview() + } + + bgView.addSubview(notificationLabel) + switch direction { + case .pointUp: + notificationLabel.snp.makeConstraints { make in + make.leading.trailing.equalToSuperview().inset(16) + make.bottom.equalToSuperview().inset(11) + } + + case .pointDown: + notificationLabel.snp.makeConstraints { make in + make.leading.trailing.equalToSuperview().inset(16) + make.top.equalToSuperview().inset(11) + } + } + } + + /// 툴팁을 방향에 맞춰 그리고 섀도우를 더하는 메서드 + private func drawToolTip() { + switch direction { + case .pointUp: + drawUpPointingToolTip() + addShadow() + + case .pointDown: + drawDownPointingTip() + addShadow() + } + } + + /// 위를 가리키는 툴팁을 만듭니다 + private func drawUpPointingToolTip() { + let textLength = notificationLabel.frame.width + let tip = UIBezierPath() + tip.move(to: CGPoint(x: midX - 8, y: 10)) + tip.addLine(to: CGPoint(x: midX, y: 0)) + tip.addLine(to: CGPoint(x: midX + 8, y: 10)) + tip.close() + + colorType.color.setFill() + tip.fill() + + let message = UIBezierPath( + roundedRect: CGRect( + x: 0, y: 10, + width: textLength + 16 + 16, + height: 35 + ), + cornerRadius: 6 + ) + + colorType.color.setFill() + message.fill() + message.close() + } + + /// 아래를 가리키는 툴팁을 만듭니다 + private func drawDownPointingTip() { + let textLength = notificationLabel.frame.width + + let tip = UIBezierPath() + tip.move(to: CGPoint(x: midX - 8, y: 35)) + tip.addLine(to: CGPoint(x: midX, y: 45)) + tip.addLine(to: CGPoint(x: midX + 8, y: 35)) + tip.close() + + colorType.color.setFill() + tip.fill() + + let message = UIBezierPath( + roundedRect: CGRect( + x: 0, y: 0, + width: textLength + 16 + 16, + height: 35 + ), + cornerRadius: 6 + ) + + colorType.color.setFill() + message.fill() + message.close() + } + + /// 툴팁의 섀도우를 더합니다 + private func addShadow() { + layer.shadowOffset = CGSize(width: 0, height: 5) + layer.shadowColor = UIColor.black.cgColor + layer.shadowOpacity = 0.2 + layer.shadowRadius = 5 + + // 섀도우를 그릴 때 드는 리소스를 줄이기 위해 캐시를 적용하는 방식 +// layer.shadowPath = UIBezierPath(rect: self.bounds).cgPath + layer.shouldRasterize = true + layer.rasterizationScale = UIScreen.main.scale + } +} + +extension UIView { + func showToolTip(color: LastLoginView.TipColor, direction: LastLoginView.TipDirection, text: String? = "최근에 이 방법으로 로그인했어요") { + // 호출하는 컴포넌트 위 또는 아래에 생성되기 위해 superview를 구합니다 + guard let superview = self.superview else { return } + + let toolTip = LastLoginView(colorType: color, direction: direction, text: text) + + superview.addSubview(toolTip) + toolTip.snp.makeConstraints { make in + if direction == .pointDown { + make.bottom.equalTo(self.snp.top).offset(-8) + make.centerX.equalToSuperview() + } else { + make.top.equalTo(self.snp.bottom).offset(8) + make.centerX.equalToSuperview() + } + } + } +} diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Main/LoginController.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Main/LoginController.swift new file mode 100644 index 00000000..b24a556f --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Main/LoginController.swift @@ -0,0 +1,88 @@ +import UIKit + +import DesignSystem +import Infrastructure + +import ReactorKit +import RxCocoa +import RxSwift +import SnapKit + +final class LoginController: BaseViewController, View { + + typealias Reactor = LoginReactor + + // MARK: - Properties + var disposeBag = DisposeBag() + + private var mainView = LoginView() +} + +// MARK: - Life Cycle +extension LoginController { + override func viewDidLoad() { + super.viewDidLoad() + setUp() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + if let lastLogin = reactor?.userDefaultService.fetch(key: "lastLogin") { + switch lastLogin { + case "kakao": + mainView.kakaoButton.showToolTip(color: .w100, direction: .pointDown, text: "최근에 이 방법으로 로그인했어요") + case "apple": + mainView.appleButton.showToolTip(color: .w100, direction: .pointUp, text: "최근에 이 방법으로 로그인했어요") + default: + break + } + } + } +} + +// MARK: - SetUp +private extension LoginController { + func setUp() { + view.addSubview(mainView) + mainView.snp.makeConstraints { make in + make.edges.equalTo(view.safeAreaLayoutGuide) + } + } +} + +// MARK: - Methods +extension LoginController { + func bind(reactor: Reactor) { + mainView.guestButton.rx.tap + .withUnretained(self) + .map { (owner, _) in + Reactor.Action.guestButtonTapped(controller: owner) + } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + mainView.kakaoButton.rx.tap + .withUnretained(self) + .map { (owner, _) in + Reactor.Action.kakaoButtonTapped(controller: owner) + } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + mainView.inquiryButton.rx.tap + .withUnretained(self) + .map { (owner, _) in + Reactor.Action.inquiryButtonTapped(controller: owner) + } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + mainView.appleButton.rx.tap + .withUnretained(self) + .map { (owner, _) in + Reactor.Action.appleButtonTapped(controller: owner) + } + .bind(to: reactor.action) + .disposed(by: disposeBag) + } +} diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Main/LoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Main/LoginReactor.swift new file mode 100644 index 00000000..56fcff23 --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Main/LoginReactor.swift @@ -0,0 +1,148 @@ +import DesignSystem +import DomainInterface +import Infrastructure + +import ReactorKit +import RxCocoa +import RxSwift + +final class LoginReactor: Reactor { + + // MARK: - Reactor + enum Action { + case kakaoButtonTapped(controller: BaseViewController) + case appleButtonTapped(controller: BaseViewController) + case guestButtonTapped(controller: BaseViewController) + case inquiryButtonTapped(controller: BaseViewController) + } + + enum Mutation { + case moveToSignUpScene(controller: BaseViewController) + case moveToHomeScene(controller: BaseViewController) + case loadView + case moveToInquiryScene(controller: BaseViewController) + } + + struct State { + } + + // MARK: - properties + + var initialState: State + var disposeBag = DisposeBag() + + private var authrizationCode: String? + + private let authAPIUseCase: AuthAPIUseCase + private let kakaoLoginUseCase: KakaoLoginUseCase + private let appleLoginUseCase: AppleLoginUseCase + + @Dependency private var keyChainService: KeyChainService + let userDefaultService = UserDefaultService() + + // MARK: - init + init( + authAPIUseCase: AuthAPIUseCase, + kakaoLoginUseCase: KakaoLoginUseCase, + appleLoginUseCase: AppleLoginUseCase + ) { + self.authAPIUseCase = authAPIUseCase + self.kakaoLoginUseCase = kakaoLoginUseCase + self.appleLoginUseCase = appleLoginUseCase + self.initialState = State() + } + + // MARK: - Reactor Methods + func mutate(action: Action) -> Observable { + switch action { + case .kakaoButtonTapped(let controller): + return loginWithKakao(controller: controller) + case .appleButtonTapped(let controller): + return loginWithApple(controller: controller) + case .guestButtonTapped(let controller): + _ = keyChainService.deleteToken(type: .accessToken) + _ = keyChainService.deleteToken(type: .refreshToken) + return Observable.just(.moveToHomeScene(controller: controller)) + case .inquiryButtonTapped(let controller): + return Observable.just(.moveToInquiryScene(controller: controller)) + } + } + + func reduce(state: State, mutation: Mutation) -> State { + switch mutation { + case .moveToSignUpScene(let controller): + let signUpController = SignUpMainController() + signUpController.reactor = SignUpMainReactor( + isFirstResponderCase: true, + authrizationCode: authrizationCode, + signUpAPIUseCase: DIContainer.resolve(SignUpAPIUseCase.self) + ) + controller.navigationController?.pushViewController(signUpController, animated: true) + case .moveToHomeScene(let controller): + let homeTabbar = WaveTabBarController() + controller.view.window?.rootViewController = homeTabbar + case .loadView: + break + case .moveToInquiryScene(let controller): + let nextController = FAQController() + nextController.reactor = FAQReactor() + controller.navigationController?.pushViewController(nextController, animated: true) + } + return state + } + + func loginWithKakao(controller: BaseViewController) -> Observable { + return kakaoLoginUseCase.fetchUserCredential() + .withUnretained(self) + .flatMap { owner, response in + return owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "kakao") + } + .withUnretained(self) + .map { [weak controller] (owner, loginResponse) in + guard let controller = controller else { return .loadView } + owner.userDefaultService.save(key: "userID", value: loginResponse.userId) + owner.userDefaultService.save(key: "socialType", value: loginResponse.socialType) + let accessTokenResult = owner.keyChainService.saveToken(type: .accessToken, value: loginResponse.accessToken) + let refreshTokenResult = owner.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) + switch accessTokenResult { + case .success: + owner.userDefaultService.save(key: "lastLogin", value: "kakao") + if loginResponse.isRegisteredUser { + return .moveToHomeScene(controller: controller) + } else { + return .moveToSignUpScene(controller: controller) + } + case .failure: + return .loadView + } + } + } + + func loginWithApple(controller: BaseViewController) -> Observable { + return appleLoginUseCase.fetchUserCredential() + .withUnretained(self) + .flatMap { owner, response in + owner.authrizationCode = response.authorizationCode + return owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "apple") + } + .withUnretained(self) + .map({ [weak controller] (owner, loginResponse) in + guard let controller = controller else { return .loadView } + owner.userDefaultService.save(key: "userID", value: loginResponse.userId) + owner.userDefaultService.save(key: "socialType", value: loginResponse.socialType) + let accessTokenResult = owner.keyChainService.saveToken(type: .accessToken, value: loginResponse.accessToken) + let refreshTokenResult = owner.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) + switch accessTokenResult { + case .success: + owner.userDefaultService.save(key: "lastLogin", value: "apple") + if loginResponse.isRegisteredUser { + return .moveToHomeScene(controller: controller) + } else { + return .moveToSignUpScene(controller: controller) + } + case .failure: + return .loadView + } + }) + } +} diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Main/LoginView.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Main/LoginView.swift new file mode 100644 index 00000000..62d5b8ad --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Main/LoginView.swift @@ -0,0 +1,129 @@ +import UIKit + +import DesignSystem + +import SnapKit + +final class LoginView: UIView { + + // MARK: - Components + let guestButton: UIButton = { + let button = UIButton(type: .system) + button.setTitle("둘러보기", for: .normal) + button.titleLabel?.font = .korFont(style: .regular, size: 14) + button.setTitleColor(.g1000, for: .normal) + return button + }() + + private let logoImageView: UIImageView = { + let view = UIImageView() + view.image = UIImage(named: "image_login_logo") + view.contentMode = .scaleAspectFit + return view + }() + + private let titleLabel: PPLabel = { + let label = PPLabel(style: .bold, fontSize: 16, text: "간편하게 SNS 로그인하고\n팝풀 서비스를 이용해보세요") + label.numberOfLines = 0 + label.textAlignment = .center + return label + }() + + let kakaoButton: PPButton = { + return PPButton(style: .kakao, text: "카카오톡으로 로그인") + }() + + private let kakaoImageView: UIImageView = { + let view = UIImageView() + view.image = UIImage(named: "icon_login_kakao") + return view + }() + + let appleButton: PPButton = { + return PPButton(style: .apple, text: "Apple로 로그인") + }() + + private let appleImageView: UIImageView = { + let view = UIImageView() + view.image = UIImage(named: "icon_login_apple") + return view + }() + + let inquiryButton: UIButton = { + let button = UIButton(type: .system) + button.setTitle("로그인이 어려우신가요?", for: .normal) + button.titleLabel?.font = .korFont(style: .regular, size: 12) + button.setTitleColor(.g1000, for: .normal) + return button + }() + + // MARK: - init + init() { + super.init(frame: .zero) + setUpConstraints() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +// MARK: - SetUp +private extension LoginView { + + func setUpConstraints() { + self.addSubview(guestButton) + guestButton.snp.makeConstraints { make in + make.top.equalToSuperview().inset(11) + make.trailing.equalToSuperview().inset(20) + } + + self.addSubview(logoImageView) + logoImageView.snp.makeConstraints { make in + make.height.equalTo(90) + make.width.equalTo(70) + make.top.equalTo(guestButton.snp.bottom).offset(75) + make.centerX.equalToSuperview() + } + + self.addSubview(titleLabel) + titleLabel.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalTo(logoImageView.snp.bottom).offset(28) + } + + self.addSubview(kakaoButton) + kakaoButton.snp.makeConstraints { make in + make.top.equalTo(titleLabel.snp.bottom).offset(156) + make.leading.trailing.equalToSuperview().inset(20) + make.height.equalTo(50) + } + + kakaoButton.addSubview(kakaoImageView) + kakaoImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.leading.equalToSuperview().inset(20) + make.size.equalTo(22) + } + + self.addSubview(appleButton) + appleButton.snp.makeConstraints { make in + make.top.equalTo(kakaoButton.snp.bottom).offset(16) + make.leading.trailing.equalToSuperview().inset(20) + make.height.equalTo(50) + } + + appleButton.addSubview(appleImageView) + appleImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.leading.equalToSuperview().inset(20) + make.size.equalTo(22) + } + + self.addSubview(inquiryButton) + inquiryButton.snp.makeConstraints { make in + make.bottom.equalToSuperview().inset(56) + make.centerX.equalToSuperview() + } + } +} diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginController.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginController.swift new file mode 100644 index 00000000..f1d42ee9 --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginController.swift @@ -0,0 +1,88 @@ +import UIKit + +import DesignSystem + +import ReactorKit +import RxCocoa +import RxSwift +import SnapKit + +final class SubLoginController: BaseViewController, View { + + typealias Reactor = SubLoginReactor + + // MARK: - Properties + var disposeBag = DisposeBag() + + private var mainView = SubLoginView() +} + +// MARK: - Life Cycle +extension SubLoginController { + override func viewDidLoad() { + super.viewDidLoad() + setUp() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + if let lastLogin = reactor?.userDefaultService.fetch(key: "lastLogin") { + switch lastLogin { + case "kakao": + mainView.kakaoButton.showToolTip(color: .w100, direction: .pointDown, text: "최근에 이 방법으로 로그인했어요") + case "apple": + mainView.appleButton.showToolTip(color: .w100, direction: .pointUp, text: "최근에 이 방법으로 로그인했어요") + default: + break + } + } + } +} + +// MARK: - SetUp +private extension SubLoginController { + func setUp() { + view.backgroundColor = .g50 + view.addSubview(mainView) + mainView.snp.makeConstraints { make in + make.edges.equalTo(view.safeAreaLayoutGuide) + } + } +} + +// MARK: - Methods +extension SubLoginController { + func bind(reactor: Reactor) { + mainView.xmarkButton.rx.tap + .withUnretained(self) + .map { (owner, _) in + Reactor.Action.xmarkButtonTapped(controller: owner) + } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + mainView.kakaoButton.rx.tap + .withUnretained(self) + .map { (owner, _) in + Reactor.Action.kakaoButtonTapped(controller: owner) + } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + mainView.inquiryButton.rx.tap + .withUnretained(self) + .map { (owner, _) in + Reactor.Action.inquiryButtonTapped(controller: owner) + } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + mainView.appleButton.rx.tap + .withUnretained(self) + .map { (owner, _) in + Reactor.Action.appleButtonTapped(controller: owner) + } + .bind(to: reactor.action) + .disposed(by: disposeBag) + } +} diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginReactor.swift new file mode 100644 index 00000000..ce0c960b --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginReactor.swift @@ -0,0 +1,144 @@ +import DesignSystem +import DomainInterface +import Infrastructure + +import ReactorKit +import RxCocoa +import RxSwift + +final class SubLoginReactor: Reactor { + + // MARK: - Reactor + enum Action { + case kakaoButtonTapped(controller: BaseViewController) + case appleButtonTapped(controller: BaseViewController) + case xmarkButtonTapped(controller: BaseViewController) + case inquiryButtonTapped(controller: BaseViewController) + } + + enum Mutation { + case moveToSignUpScene(controller: BaseViewController) + case dismissScene(controller: BaseViewController) + case loadView + case moveToInquiryScene(controller: BaseViewController) + } + + struct State { + } + + // MARK: - properties + + var initialState: State + var disposeBag = DisposeBag() + + private var authrizationCode: String? + + private let authAPIUseCase: AuthAPIUseCase + private let kakaoLoginUseCase: KakaoLoginUseCase + private let appleLoginUseCase: AppleLoginUseCase + @Dependency private var keyChainService: KeyChainService + let userDefaultService = UserDefaultService() + + // MARK: - init + init( + authAPIUseCase: AuthAPIUseCase, + kakaoLoginUseCase: KakaoLoginUseCase, + appleLoginUseCase: AppleLoginUseCase + ) { + self.authAPIUseCase = authAPIUseCase + self.kakaoLoginUseCase = kakaoLoginUseCase + self.appleLoginUseCase = appleLoginUseCase + self.initialState = State() + } + + // MARK: - Reactor Methods + func mutate(action: Action) -> Observable { + switch action { + case .kakaoButtonTapped(let controller): + return loginWithKakao(controller: controller) + case .appleButtonTapped(let controller): + return loginWithApple(controller: controller) + case .xmarkButtonTapped(let controller): + return Observable.just(.dismissScene(controller: controller)) + case .inquiryButtonTapped(let controller): + return Observable.just(.moveToInquiryScene(controller: controller)) + } + } + + func reduce(state: State, mutation: Mutation) -> State { + switch mutation { + case .moveToSignUpScene(let controller): + let signUpController = SignUpMainController() + signUpController.reactor = SignUpMainReactor( + isFirstResponderCase: false, + authrizationCode: authrizationCode, + signUpAPIUseCase: DIContainer.resolve(SignUpAPIUseCase.self) + ) + controller.navigationController?.pushViewController(signUpController, animated: true) + case .dismissScene(let controller): + controller.dismiss(animated: true) + case .loadView: + break + case .moveToInquiryScene(let controller): + let nextController = FAQController() + nextController.reactor = FAQReactor() + controller.navigationController?.pushViewController(nextController, animated: true) + } + return state + } + + func loginWithKakao(controller: BaseViewController) -> Observable { + return kakaoLoginUseCase.fetchUserCredential() + .withUnretained(self) + .flatMap { owner, response in + owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "kakao") + } + .withUnretained(self) + .map { [weak controller] (owner, loginResponse) in + guard let controller = controller else { return .loadView } + owner.userDefaultService.save(key: "userID", value: loginResponse.userId) + owner.userDefaultService.save(key: "socialType", value: loginResponse.socialType) + let accessTokenResult = owner.keyChainService.saveToken(type: .accessToken, value: loginResponse.accessToken) + let refreshTokenResult = owner.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) + switch accessTokenResult { + case .success: + owner.userDefaultService.save(key: "lastLogin", value: "kakao") + if loginResponse.isRegisteredUser { + return .dismissScene(controller: controller) + } else { + return .moveToSignUpScene(controller: controller) + } + case .failure: + return .loadView + } + } + } + + func loginWithApple(controller: BaseViewController) -> Observable { + return appleLoginUseCase.fetchUserCredential() + .withUnretained(self) + .flatMap { owner, response in + owner.authrizationCode = response.authorizationCode + return owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "apple") + } + .withUnretained(self) + .map { [weak controller] (owner, loginResponse) in + guard let controller = controller else { return .loadView } + owner.userDefaultService.save(key: "userID", value: loginResponse.userId) + owner.userDefaultService.save(key: "socialType", value: loginResponse.socialType) + let accessTokenResult = owner.keyChainService.saveToken(type: .accessToken, value: loginResponse.accessToken) + let refreshTokenResult = owner.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) + switch accessTokenResult { + case .success: + owner.userDefaultService.save(key: "lastLogin", value: "apple") + if loginResponse.isRegisteredUser { + return .dismissScene(controller: controller) + } else { + return .moveToSignUpScene(controller: controller) + } + case .failure: + return .loadView + } + } + } +} diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginView.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginView.swift new file mode 100644 index 00000000..cea96869 --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginView.swift @@ -0,0 +1,130 @@ +import UIKit + +import DesignSystem + +import SnapKit + +final class SubLoginView: UIView { + + // MARK: - Components + let xmarkButton: UIButton = { + let button = UIButton(type: .system) + button.setImage(UIImage(named: "icon_xmark"), for: .normal) + button.tintColor = .g1000 + return button + }() + + private let logoImageView: UIImageView = { + let view = UIImageView() + view.image = UIImage(named: "image_login_logo") + view.contentMode = .scaleAspectFit + return view + }() + + private let titleLabel: PPLabel = { + let label = PPLabel(style: .bold, fontSize: 16, text: "간편하게 SNS 로그인하고\n공감가는 코멘트에 반응해볼까요?\n다른 코멘트를 확인해볼까요?") + label.setLineHeightText(text: "간편하게 SNS 로그인하고\n공감가는 코멘트에 반응해볼까요?\n다른 코멘트를 확인해볼까요?", font: .korFont(style: .bold, size: 16), lineHeight: 1.3) + label.numberOfLines = 0 + label.textAlignment = .center + return label + }() + + let kakaoButton: PPButton = { + return PPButton(style: .kakao, text: "카카오톡으로 로그인") + }() + + private let kakaoImageView: UIImageView = { + let view = UIImageView() + view.image = UIImage(named: "icon_login_kakao") + return view + }() + + let appleButton: PPButton = { + return PPButton(style: .apple, text: "Apple로 로그인") + }() + + private let appleImageView: UIImageView = { + let view = UIImageView() + view.image = UIImage(named: "icon_login_apple") + return view + }() + + let inquiryButton: UIButton = { + let button = UIButton(type: .system) + button.setTitle("로그인이 어려우신가요?", for: .normal) + button.titleLabel?.font = .korFont(style: .regular, size: 12) + button.setTitleColor(.g1000, for: .normal) + return button + }() + + // MARK: - init + init() { + super.init(frame: .zero) + setUpConstraints() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +// MARK: - SetUp +private extension SubLoginView { + + func setUpConstraints() { + self.addSubview(xmarkButton) + xmarkButton.snp.makeConstraints { make in + make.top.equalToSuperview().inset(11) + make.trailing.equalToSuperview().inset(20) + make.size.equalTo(32) + } + + self.addSubview(logoImageView) + logoImageView.snp.makeConstraints { make in + make.height.equalTo(90) + make.width.equalTo(70) + make.top.equalTo(xmarkButton.snp.bottom).offset(75) + make.centerX.equalToSuperview() + } + + self.addSubview(titleLabel) + titleLabel.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalTo(logoImageView.snp.bottom).offset(28) + } + + self.addSubview(kakaoButton) + kakaoButton.snp.makeConstraints { make in + make.top.equalTo(titleLabel.snp.bottom).offset(156) + make.leading.trailing.equalToSuperview().inset(20) + make.height.equalTo(50) + } + + kakaoButton.addSubview(kakaoImageView) + kakaoImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.leading.equalToSuperview().inset(20) + make.size.equalTo(22) + } + + self.addSubview(appleButton) + appleButton.snp.makeConstraints { make in + make.top.equalTo(kakaoButton.snp.bottom).offset(16) + make.leading.trailing.equalToSuperview().inset(20) + make.height.equalTo(50) + } + + appleButton.addSubview(appleImageView) + appleImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.leading.equalToSuperview().inset(20) + make.size.equalTo(22) + } + + self.addSubview(inquiryButton) + inquiryButton.snp.makeConstraints { make in + make.bottom.equalToSuperview().inset(56) + make.centerX.equalToSuperview() + } + } +} From 381cadfa8e0ca1371b1e8af9f34da58a336e5d45 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Jul 2025 15:23:42 +0900 Subject: [PATCH 10/56] =?UTF-8?q?refactor/#160:=20=EA=B8=B0=EC=A1=B4?= =?UTF-8?q?=EC=9D=98=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=ED=99=94=EB=A9=B4?= =?UTF-8?q?=EC=97=90=20=EC=BB=A8=EB=B2=A4=EC=85=98=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoginFeature.xcodeproj/project.pbxproj | 44 ++++++ .../LoginFeature/Login/LoginView.swift | 109 ++++++++++++++- .../LoginFeature/Login/Main/LoginView.swift | 129 ------------------ 3 files changed, 148 insertions(+), 134 deletions(-) delete mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Main/LoginView.swift diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/project.pbxproj index 66ca0953..df3cf150 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/project.pbxproj @@ -18,6 +18,10 @@ 05A7C1D32E267AF80010F1CD /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7C1D22E267AF80010F1CD /* ReactorKit */; }; 05A7C1D62E267B130010F1CD /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7C1D52E267B130010F1CD /* RxCocoa */; }; 05A7C1D82E267B130010F1CD /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7C1D72E267B130010F1CD /* RxSwift */; }; + 05A7CBB62E277AD20010F1CD /* SnapKit-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7CBB52E277AD20010F1CD /* SnapKit-Dynamic */; }; + 05A7CBB92E277AE30010F1CD /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7CBB82E277AE30010F1CD /* Then */; }; + 05A7CBBB2E277AF20010F1CD /* SnapKit-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7CBBA2E277AF20010F1CD /* SnapKit-Dynamic */; }; + 05A7CBBC2E277AF20010F1CD /* SnapKit-Dynamic in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 05A7CBBA2E277AF20010F1CD /* SnapKit-Dynamic */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -54,6 +58,7 @@ 05A7C1BD2E2679FC0010F1CD /* Infrastructure.framework in Embed Frameworks */, 05A7C1B32E26799A0010F1CD /* LoginFeature.framework in Embed Frameworks */, 05A7C1B72E26799A0010F1CD /* LoginFeatureInterface.framework in Embed Frameworks */, + 05A7CBBC2E277AF20010F1CD /* SnapKit-Dynamic in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -104,6 +109,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 05A7CBB62E277AD20010F1CD /* SnapKit-Dynamic in Frameworks */, + 05A7CBB92E277AE30010F1CD /* Then in Frameworks */, 05A7C1D62E267B130010F1CD /* RxCocoa in Frameworks */, 05A7C1D32E267AF80010F1CD /* ReactorKit in Frameworks */, 05A7C1D82E267B130010F1CD /* RxSwift in Frameworks */, @@ -126,6 +133,7 @@ 05A7C1BC2E2679FC0010F1CD /* Infrastructure.framework in Frameworks */, 05A7C1B22E26799A0010F1CD /* LoginFeature.framework in Frameworks */, 05A7C1B62E26799A0010F1CD /* LoginFeatureInterface.framework in Frameworks */, + 05A7CBBB2E277AF20010F1CD /* SnapKit-Dynamic in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -204,6 +212,8 @@ 05A7C1D22E267AF80010F1CD /* ReactorKit */, 05A7C1D52E267B130010F1CD /* RxCocoa */, 05A7C1D72E267B130010F1CD /* RxSwift */, + 05A7CBB52E277AD20010F1CD /* SnapKit-Dynamic */, + 05A7CBB82E277AE30010F1CD /* Then */, ); productName = LoginFeature; productReference = 05A7C15F2E26768A0010F1CD /* LoginFeature.framework */; @@ -252,6 +262,7 @@ ); name = LoginFeatureDemo; packageProductDependencies = ( + 05A7CBBA2E277AF20010F1CD /* SnapKit-Dynamic */, ); productName = LoginFeatureDemo; productReference = 05A7C1922E26771A0010F1CD /* LoginFeatureDemo.app */; @@ -290,6 +301,8 @@ packageReferences = ( 05A7C1D12E267AF80010F1CD /* XCRemoteSwiftPackageReference "ReactorKit" */, 05A7C1D42E267B130010F1CD /* XCRemoteSwiftPackageReference "RxSwift" */, + 05A7CBB42E277AD20010F1CD /* XCRemoteSwiftPackageReference "SnapKit" */, + 05A7CBB72E277AE30010F1CD /* XCRemoteSwiftPackageReference "Then" */, ); preferredProjectObjectVersion = 77; productRefGroup = 05A7C1602E26768A0010F1CD /* Products */; @@ -779,6 +792,22 @@ minimumVersion = 6.9.0; }; }; + 05A7CBB42E277AD20010F1CD /* XCRemoteSwiftPackageReference "SnapKit" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/SnapKit/SnapKit"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 5.7.1; + }; + }; + 05A7CBB72E277AE30010F1CD /* XCRemoteSwiftPackageReference "Then" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/devxoul/Then"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 3.0.0; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -797,6 +826,21 @@ package = 05A7C1D42E267B130010F1CD /* XCRemoteSwiftPackageReference "RxSwift" */; productName = RxSwift; }; + 05A7CBB52E277AD20010F1CD /* SnapKit-Dynamic */ = { + isa = XCSwiftPackageProductDependency; + package = 05A7CBB42E277AD20010F1CD /* XCRemoteSwiftPackageReference "SnapKit" */; + productName = "SnapKit-Dynamic"; + }; + 05A7CBB82E277AE30010F1CD /* Then */ = { + isa = XCSwiftPackageProductDependency; + package = 05A7CBB72E277AE30010F1CD /* XCRemoteSwiftPackageReference "Then" */; + productName = Then; + }; + 05A7CBBA2E277AF20010F1CD /* SnapKit-Dynamic */ = { + isa = XCSwiftPackageProductDependency; + package = 05A7CBB42E277AD20010F1CD /* XCRemoteSwiftPackageReference "SnapKit" */; + productName = "SnapKit-Dynamic"; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 05A7C1562E26768A0010F1CD /* Project object */; diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginView.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginView.swift index 1ca859d3..501ad81f 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginView.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginView.swift @@ -1,9 +1,50 @@ import UIKit +import DesignSystem + import SnapKit +import Then final class LoginView: UIView { - // MARK: - Properties + + // MARK: - Components + let guestButton = UIButton(type: .system).then { + $0.setTitle("둘러보기", for: .normal) + $0.titleLabel?.font = .korFont(style: .regular, size: 14) + $0.setTitleColor(.g1000, for: .normal) + } + + private let logoImageView = UIImageView().then { + $0.image = UIImage(named: "image_login_logo") + $0.contentMode = .scaleAspectFit + } + + private let titleLabel = PPLabel( + style: .bold, + fontSize: 16, + text: "간편하게 SNS 로그인하고\n팝풀 서비스를 이용해보세요" + ).then { + $0.numberOfLines = 0 + $0.textAlignment = .center + } + + let kakaoButton = PPButton(style: .kakao, text: "카카오톡으로 로그인") + + private let kakaoImageView = UIImageView().then { + $0.image = UIImage(named: "icon_login_kakao") + } + + let appleButton = PPButton(style: .apple, text: "Apple로 로그인") + + private let appleImageView = UIImageView().then { + $0.image = UIImage(named: "icon_login_apple") + } + + let inquiryButton = UIButton(type: .system).then { + $0.setTitle("로그인이 어려우신가요?", for: .normal) + $0.titleLabel?.font = .korFont(style: .regular, size: 12) + $0.setTitleColor(.g1000, for: .normal) + } // MARK: - init init() { @@ -13,7 +54,7 @@ final class LoginView: UIView { self.setupConstraints() self.configureUI() } - + required init?(coder: NSCoder) { fatalError("\(#file), \(#function) Error") } @@ -21,10 +62,68 @@ final class LoginView: UIView { // MARK: - SetUp private extension LoginView { - func addViews() { } - func setupConstraints() { } + func addViews() { + [guestButton, logoImageView, titleLabel, kakaoButton, appleButton, inquiryButton].forEach { + self.addSubview($0) + } + + [kakaoImageView].forEach { + kakaoButton.addSubview($0) + } + + [appleImageView].forEach { + appleButton.addSubview($0) + } + } + + func setupConstraints() { + guestButton.snp.makeConstraints { make in + make.top.equalToSuperview().inset(11) + make.trailing.equalToSuperview().inset(20) + } + + logoImageView.snp.makeConstraints { make in + make.height.equalTo(90) + make.width.equalTo(70) + make.top.equalTo(guestButton.snp.bottom).offset(75) + make.centerX.equalToSuperview() + } + + titleLabel.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalTo(logoImageView.snp.bottom).offset(28) + } + + kakaoButton.snp.makeConstraints { make in + make.top.equalTo(titleLabel.snp.bottom).offset(156) + make.leading.trailing.equalToSuperview().inset(20) + make.height.equalTo(50) + } + + kakaoImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.leading.equalToSuperview().inset(20) + make.size.equalTo(22) + } + + appleButton.snp.makeConstraints { make in + make.top.equalTo(kakaoButton.snp.bottom).offset(16) + make.leading.trailing.equalToSuperview().inset(20) + make.height.equalTo(50) + } + + appleImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.leading.equalToSuperview().inset(20) + make.size.equalTo(22) + } + + inquiryButton.snp.makeConstraints { make in + make.bottom.equalToSuperview().inset(56) + make.centerX.equalToSuperview() + } + } func configureUI() { } } - diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Main/LoginView.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Main/LoginView.swift deleted file mode 100644 index 62d5b8ad..00000000 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Main/LoginView.swift +++ /dev/null @@ -1,129 +0,0 @@ -import UIKit - -import DesignSystem - -import SnapKit - -final class LoginView: UIView { - - // MARK: - Components - let guestButton: UIButton = { - let button = UIButton(type: .system) - button.setTitle("둘러보기", for: .normal) - button.titleLabel?.font = .korFont(style: .regular, size: 14) - button.setTitleColor(.g1000, for: .normal) - return button - }() - - private let logoImageView: UIImageView = { - let view = UIImageView() - view.image = UIImage(named: "image_login_logo") - view.contentMode = .scaleAspectFit - return view - }() - - private let titleLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 16, text: "간편하게 SNS 로그인하고\n팝풀 서비스를 이용해보세요") - label.numberOfLines = 0 - label.textAlignment = .center - return label - }() - - let kakaoButton: PPButton = { - return PPButton(style: .kakao, text: "카카오톡으로 로그인") - }() - - private let kakaoImageView: UIImageView = { - let view = UIImageView() - view.image = UIImage(named: "icon_login_kakao") - return view - }() - - let appleButton: PPButton = { - return PPButton(style: .apple, text: "Apple로 로그인") - }() - - private let appleImageView: UIImageView = { - let view = UIImageView() - view.image = UIImage(named: "icon_login_apple") - return view - }() - - let inquiryButton: UIButton = { - let button = UIButton(type: .system) - button.setTitle("로그인이 어려우신가요?", for: .normal) - button.titleLabel?.font = .korFont(style: .regular, size: 12) - button.setTitleColor(.g1000, for: .normal) - return button - }() - - // MARK: - init - init() { - super.init(frame: .zero) - setUpConstraints() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp -private extension LoginView { - - func setUpConstraints() { - self.addSubview(guestButton) - guestButton.snp.makeConstraints { make in - make.top.equalToSuperview().inset(11) - make.trailing.equalToSuperview().inset(20) - } - - self.addSubview(logoImageView) - logoImageView.snp.makeConstraints { make in - make.height.equalTo(90) - make.width.equalTo(70) - make.top.equalTo(guestButton.snp.bottom).offset(75) - make.centerX.equalToSuperview() - } - - self.addSubview(titleLabel) - titleLabel.snp.makeConstraints { make in - make.centerX.equalToSuperview() - make.top.equalTo(logoImageView.snp.bottom).offset(28) - } - - self.addSubview(kakaoButton) - kakaoButton.snp.makeConstraints { make in - make.top.equalTo(titleLabel.snp.bottom).offset(156) - make.leading.trailing.equalToSuperview().inset(20) - make.height.equalTo(50) - } - - kakaoButton.addSubview(kakaoImageView) - kakaoImageView.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.leading.equalToSuperview().inset(20) - make.size.equalTo(22) - } - - self.addSubview(appleButton) - appleButton.snp.makeConstraints { make in - make.top.equalTo(kakaoButton.snp.bottom).offset(16) - make.leading.trailing.equalToSuperview().inset(20) - make.height.equalTo(50) - } - - appleButton.addSubview(appleImageView) - appleImageView.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.leading.equalToSuperview().inset(20) - make.size.equalTo(22) - } - - self.addSubview(inquiryButton) - inquiryButton.snp.makeConstraints { make in - make.bottom.equalToSuperview().inset(56) - make.centerX.equalToSuperview() - } - } -} From 00877291f9f389b71a1b13098af556fb31f34bee Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Jul 2025 15:25:17 +0900 Subject: [PATCH 11/56] =?UTF-8?q?refactor/#160:=20=EA=B8=B0=EC=A1=B4?= =?UTF-8?q?=EC=9D=98=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EB=A6=AC=EC=95=A1?= =?UTF-8?q?=ED=84=B0=20=EC=9D=B4=EB=8F=99=20=EB=B0=8F=20=EC=BB=A8=EB=B2=A4?= =?UTF-8?q?=EC=85=98=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoginFeature/Login/LoginReactor.swift | 129 +++++++++++++-- .../Login/Main/LoginReactor.swift | 148 ------------------ 2 files changed, 120 insertions(+), 157 deletions(-) delete mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Main/LoginReactor.swift diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift index fa329d5e..d6d0cb0c 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift @@ -1,36 +1,147 @@ +import DesignSystem +import DomainInterface +import Infrastructure + import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class LoginReactor: Reactor { // MARK: - Reactor - enum Action { } + enum Action { + case kakaoButtonTapped(controller: BaseViewController) + case appleButtonTapped(controller: BaseViewController) + case guestButtonTapped(controller: BaseViewController) + case inquiryButtonTapped(controller: BaseViewController) + } - enum Mutation { } + enum Mutation { + case moveToSignUpScene(controller: BaseViewController) + case moveToHomeScene(controller: BaseViewController) + case loadView + case moveToInquiryScene(controller: BaseViewController) + } struct State { } // MARK: - properties + var initialState: State var disposeBag = DisposeBag() + private var authrizationCode: String? + + private let authAPIUseCase: AuthAPIUseCase + private let kakaoLoginUseCase: KakaoLoginUseCase + private let appleLoginUseCase: AppleLoginUseCase + + @Dependency private var keyChainService: KeyChainService + let userDefaultService = UserDefaultService() + // MARK: - init - init() { + init( + authAPIUseCase: AuthAPIUseCase, + kakaoLoginUseCase: KakaoLoginUseCase, + appleLoginUseCase: AppleLoginUseCase + ) { + self.authAPIUseCase = authAPIUseCase + self.kakaoLoginUseCase = kakaoLoginUseCase + self.appleLoginUseCase = appleLoginUseCase self.initialState = State() } // MARK: - Reactor Methods func mutate(action: Action) -> Observable { - switch action { } + switch action { + case .kakaoButtonTapped(let controller): + return loginWithKakao(controller: controller) + case .appleButtonTapped(let controller): + return loginWithApple(controller: controller) + case .guestButtonTapped(let controller): + _ = keyChainService.deleteToken(type: .accessToken) + _ = keyChainService.deleteToken(type: .refreshToken) + return Observable.just(.moveToHomeScene(controller: controller)) + case .inquiryButtonTapped(let controller): + return Observable.just(.moveToInquiryScene(controller: controller)) + } } func reduce(state: State, mutation: Mutation) -> State { - var newState = state + switch mutation { + case .moveToSignUpScene(let controller): + let signUpController = SignUpMainController() + signUpController.reactor = SignUpMainReactor( + isFirstResponderCase: true, + authrizationCode: authrizationCode, + signUpAPIUseCase: DIContainer.resolve(SignUpAPIUseCase.self) + ) + controller.navigationController?.pushViewController(signUpController, animated: true) + case .moveToHomeScene(let controller): + let homeTabbar = WaveTabBarController() + controller.view.window?.rootViewController = homeTabbar + case .loadView: + break + case .moveToInquiryScene(let controller): + let nextController = FAQController() + nextController.reactor = FAQReactor() + controller.navigationController?.pushViewController(nextController, animated: true) + } + return state + } - switch mutation { } + func loginWithKakao(controller: BaseViewController) -> Observable { + return kakaoLoginUseCase.fetchUserCredential() + .withUnretained(self) + .flatMap { owner, response in + return owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "kakao") + } + .withUnretained(self) + .map { [weak controller] (owner, loginResponse) in + guard let controller = controller else { return .loadView } + owner.userDefaultService.save(key: "userID", value: loginResponse.userId) + owner.userDefaultService.save(key: "socialType", value: loginResponse.socialType) + let accessTokenResult = owner.keyChainService.saveToken(type: .accessToken, value: loginResponse.accessToken) + let refreshTokenResult = owner.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) + switch accessTokenResult { + case .success: + owner.userDefaultService.save(key: "lastLogin", value: "kakao") + if loginResponse.isRegisteredUser { + return .moveToHomeScene(controller: controller) + } else { + return .moveToSignUpScene(controller: controller) + } + case .failure: + return .loadView + } + } + } - return newState + func loginWithApple(controller: BaseViewController) -> Observable { + return appleLoginUseCase.fetchUserCredential() + .withUnretained(self) + .flatMap { owner, response in + owner.authrizationCode = response.authorizationCode + return owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "apple") + } + .withUnretained(self) + .map({ [weak controller] (owner, loginResponse) in + guard let controller = controller else { return .loadView } + owner.userDefaultService.save(key: "userID", value: loginResponse.userId) + owner.userDefaultService.save(key: "socialType", value: loginResponse.socialType) + let accessTokenResult = owner.keyChainService.saveToken(type: .accessToken, value: loginResponse.accessToken) + let refreshTokenResult = owner.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) + switch accessTokenResult { + case .success: + owner.userDefaultService.save(key: "lastLogin", value: "apple") + if loginResponse.isRegisteredUser { + return .moveToHomeScene(controller: controller) + } else { + return .moveToSignUpScene(controller: controller) + } + case .failure: + return .loadView + } + }) } } - diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Main/LoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Main/LoginReactor.swift deleted file mode 100644 index 56fcff23..00000000 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Main/LoginReactor.swift +++ /dev/null @@ -1,148 +0,0 @@ -import DesignSystem -import DomainInterface -import Infrastructure - -import ReactorKit -import RxCocoa -import RxSwift - -final class LoginReactor: Reactor { - - // MARK: - Reactor - enum Action { - case kakaoButtonTapped(controller: BaseViewController) - case appleButtonTapped(controller: BaseViewController) - case guestButtonTapped(controller: BaseViewController) - case inquiryButtonTapped(controller: BaseViewController) - } - - enum Mutation { - case moveToSignUpScene(controller: BaseViewController) - case moveToHomeScene(controller: BaseViewController) - case loadView - case moveToInquiryScene(controller: BaseViewController) - } - - struct State { - } - - // MARK: - properties - - var initialState: State - var disposeBag = DisposeBag() - - private var authrizationCode: String? - - private let authAPIUseCase: AuthAPIUseCase - private let kakaoLoginUseCase: KakaoLoginUseCase - private let appleLoginUseCase: AppleLoginUseCase - - @Dependency private var keyChainService: KeyChainService - let userDefaultService = UserDefaultService() - - // MARK: - init - init( - authAPIUseCase: AuthAPIUseCase, - kakaoLoginUseCase: KakaoLoginUseCase, - appleLoginUseCase: AppleLoginUseCase - ) { - self.authAPIUseCase = authAPIUseCase - self.kakaoLoginUseCase = kakaoLoginUseCase - self.appleLoginUseCase = appleLoginUseCase - self.initialState = State() - } - - // MARK: - Reactor Methods - func mutate(action: Action) -> Observable { - switch action { - case .kakaoButtonTapped(let controller): - return loginWithKakao(controller: controller) - case .appleButtonTapped(let controller): - return loginWithApple(controller: controller) - case .guestButtonTapped(let controller): - _ = keyChainService.deleteToken(type: .accessToken) - _ = keyChainService.deleteToken(type: .refreshToken) - return Observable.just(.moveToHomeScene(controller: controller)) - case .inquiryButtonTapped(let controller): - return Observable.just(.moveToInquiryScene(controller: controller)) - } - } - - func reduce(state: State, mutation: Mutation) -> State { - switch mutation { - case .moveToSignUpScene(let controller): - let signUpController = SignUpMainController() - signUpController.reactor = SignUpMainReactor( - isFirstResponderCase: true, - authrizationCode: authrizationCode, - signUpAPIUseCase: DIContainer.resolve(SignUpAPIUseCase.self) - ) - controller.navigationController?.pushViewController(signUpController, animated: true) - case .moveToHomeScene(let controller): - let homeTabbar = WaveTabBarController() - controller.view.window?.rootViewController = homeTabbar - case .loadView: - break - case .moveToInquiryScene(let controller): - let nextController = FAQController() - nextController.reactor = FAQReactor() - controller.navigationController?.pushViewController(nextController, animated: true) - } - return state - } - - func loginWithKakao(controller: BaseViewController) -> Observable { - return kakaoLoginUseCase.fetchUserCredential() - .withUnretained(self) - .flatMap { owner, response in - return owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "kakao") - } - .withUnretained(self) - .map { [weak controller] (owner, loginResponse) in - guard let controller = controller else { return .loadView } - owner.userDefaultService.save(key: "userID", value: loginResponse.userId) - owner.userDefaultService.save(key: "socialType", value: loginResponse.socialType) - let accessTokenResult = owner.keyChainService.saveToken(type: .accessToken, value: loginResponse.accessToken) - let refreshTokenResult = owner.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) - switch accessTokenResult { - case .success: - owner.userDefaultService.save(key: "lastLogin", value: "kakao") - if loginResponse.isRegisteredUser { - return .moveToHomeScene(controller: controller) - } else { - return .moveToSignUpScene(controller: controller) - } - case .failure: - return .loadView - } - } - } - - func loginWithApple(controller: BaseViewController) -> Observable { - return appleLoginUseCase.fetchUserCredential() - .withUnretained(self) - .flatMap { owner, response in - owner.authrizationCode = response.authorizationCode - return owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "apple") - } - .withUnretained(self) - .map({ [weak controller] (owner, loginResponse) in - guard let controller = controller else { return .loadView } - owner.userDefaultService.save(key: "userID", value: loginResponse.userId) - owner.userDefaultService.save(key: "socialType", value: loginResponse.socialType) - let accessTokenResult = owner.keyChainService.saveToken(type: .accessToken, value: loginResponse.accessToken) - let refreshTokenResult = owner.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) - switch accessTokenResult { - case .success: - owner.userDefaultService.save(key: "lastLogin", value: "apple") - if loginResponse.isRegisteredUser { - return .moveToHomeScene(controller: controller) - } else { - return .moveToSignUpScene(controller: controller) - } - case .failure: - return .loadView - } - }) - } -} From da9282600fe7d2899d84f4811b31200b4473b4b6 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Jul 2025 16:56:10 +0900 Subject: [PATCH 12/56] =?UTF-8?q?refactor/#160:=20SignUp=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EC=9D=B4=EB=8F=99=20=ED=8C=A9=ED=84=B0=EB=A6=AC=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=ED=8C=A8=ED=84=B4=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoginFeature/Login/LoginReactor.swift | 12 +++++----- .../FactoryImpl/SignUpFactoryImpl.swift | 23 +++++++++++++++++++ .../Factory/SignUpFactory.swift | 7 ++++++ 3 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/FactoryImpl/SignUpFactoryImpl.swift create mode 100644 Poppool/PresentationLayer/Presentation/PresentationInterface/Factory/SignUpFactory.swift diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift index d6d0cb0c..de08e2a1 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift @@ -1,6 +1,7 @@ import DesignSystem import DomainInterface import Infrastructure +import PresentationInterface import ReactorKit import RxCocoa @@ -70,13 +71,12 @@ final class LoginReactor: Reactor { func reduce(state: State, mutation: Mutation) -> State { switch mutation { case .moveToSignUpScene(let controller): - let signUpController = SignUpMainController() - signUpController.reactor = SignUpMainReactor( - isFirstResponderCase: true, - authrizationCode: authrizationCode, - signUpAPIUseCase: DIContainer.resolve(SignUpAPIUseCase.self) + @Dependency var factory: SignUpFactory + controller.navigationController?.pushViewController( + factory.make(isFirstResponder: true, authrizationCode: authrizationCode), + animated: true ) - controller.navigationController?.pushViewController(signUpController, animated: true) + case .moveToHomeScene(let controller): let homeTabbar = WaveTabBarController() controller.view.window?.rootViewController = homeTabbar diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/FactoryImpl/SignUpFactoryImpl.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/FactoryImpl/SignUpFactoryImpl.swift new file mode 100644 index 00000000..bcac3cbd --- /dev/null +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/FactoryImpl/SignUpFactoryImpl.swift @@ -0,0 +1,23 @@ +import UIKit + +import DesignSystem +import DomainInterface +import PresentationInterface +import Infrastructure + +public final class SignUpFactoryImpl: SignUpFactory { + + public init() { } + + public func make(isFirstResponder: Bool, authrizationCode: String?) -> DesignSystem.BaseTabmanController { + let viewController = SignUpMainController() + + viewController.reactor = SignUpMainReactor( + isFirstResponderCase: isFirstResponder, + authrizationCode: authrizationCode, + signUpAPIUseCase: DIContainer.resolve(SignUpAPIUseCase.self) + ) + + return viewController + } +} diff --git a/Poppool/PresentationLayer/Presentation/PresentationInterface/Factory/SignUpFactory.swift b/Poppool/PresentationLayer/Presentation/PresentationInterface/Factory/SignUpFactory.swift new file mode 100644 index 00000000..4e5cfd61 --- /dev/null +++ b/Poppool/PresentationLayer/Presentation/PresentationInterface/Factory/SignUpFactory.swift @@ -0,0 +1,7 @@ +import UIKit + +import DesignSystem + +public protocol SignUpFactory { + func make(isFirstResponder: Bool, authrizationCode: String?) -> BaseTabmanController +} From b6a185c220ea99c788ab48887fc22c539d39d7d9 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Jul 2025 17:01:21 +0900 Subject: [PATCH 13/56] =?UTF-8?q?refactor/#160:=20FAQ=20=ED=99=94=EB=A9=B4?= =?UTF-8?q?=20=EC=9D=B4=EB=8F=99=20=ED=8C=A9=ED=86=A0=EB=A6=AC=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=ED=8C=A8=ED=84=B4=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoginFeature/Login/LoginReactor.swift | 7 +++---- .../MyPage/FAQ/FactoryImpl/FAQFactoryImpl.swift | 13 +++++++++++++ .../PresentationInterface/Factory/FAQFactory.swift | 5 +++++ 3 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/FactoryImpl/FAQFactoryImpl.swift create mode 100644 Poppool/PresentationLayer/Presentation/PresentationInterface/Factory/FAQFactory.swift diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift index de08e2a1..6c41ec2c 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift @@ -76,16 +76,15 @@ final class LoginReactor: Reactor { factory.make(isFirstResponder: true, authrizationCode: authrizationCode), animated: true ) - + case .moveToHomeScene(let controller): let homeTabbar = WaveTabBarController() controller.view.window?.rootViewController = homeTabbar case .loadView: break case .moveToInquiryScene(let controller): - let nextController = FAQController() - nextController.reactor = FAQReactor() - controller.navigationController?.pushViewController(nextController, animated: true) + @Dependency var factory: FAQFactory + controller.navigationController?.pushViewController(factory.make(), animated: true) } return state } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/FactoryImpl/FAQFactoryImpl.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/FactoryImpl/FAQFactoryImpl.swift new file mode 100644 index 00000000..2ed5471c --- /dev/null +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/FactoryImpl/FAQFactoryImpl.swift @@ -0,0 +1,13 @@ +import DesignSystem +import PresentationInterface + +public final class FAQFactoryImpl: FAQFactory { + public init() { } + + public func make() -> BaseViewController { + let viewController = FAQController() + viewController.reactor = FAQReactor() + + return viewController + } +} diff --git a/Poppool/PresentationLayer/Presentation/PresentationInterface/Factory/FAQFactory.swift b/Poppool/PresentationLayer/Presentation/PresentationInterface/Factory/FAQFactory.swift new file mode 100644 index 00000000..81d685f4 --- /dev/null +++ b/Poppool/PresentationLayer/Presentation/PresentationInterface/Factory/FAQFactory.swift @@ -0,0 +1,5 @@ +import DesignSystem + +public protocol FAQFactory { + func make() -> BaseViewController +} From 1f8983d3571110709ad9efdb806e28666e1419bf Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Jul 2025 17:01:42 +0900 Subject: [PATCH 14/56] =?UTF-8?q?style/#160:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=ED=94=84=EB=A0=88=EC=9E=84?= =?UTF-8?q?=EC=9B=8C=ED=81=AC=20import=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PresentationInterface/Factory/SignUpFactory.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/PresentationInterface/Factory/SignUpFactory.swift b/Poppool/PresentationLayer/Presentation/PresentationInterface/Factory/SignUpFactory.swift index 4e5cfd61..a2d2a667 100644 --- a/Poppool/PresentationLayer/Presentation/PresentationInterface/Factory/SignUpFactory.swift +++ b/Poppool/PresentationLayer/Presentation/PresentationInterface/Factory/SignUpFactory.swift @@ -1,5 +1,3 @@ -import UIKit - import DesignSystem public protocol SignUpFactory { From 939bbbfb98443ffbf76c3a16832f05f503dee9f1 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Jul 2025 17:05:45 +0900 Subject: [PATCH 15/56] =?UTF-8?q?refactor/#160:=20=ED=83=AD=EB=B0=94=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EC=A0=84=ED=99=98=EC=9D=84=20=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=20=EB=A9=94=EC=84=9C=EB=93=9C=20=ED=8C=A8?= =?UTF-8?q?=ED=84=B4=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoginFeature/Login/LoginReactor.swift | 6 ++++-- .../FactoryImpl/WaveTabbarFactoryImpl.swift | 11 +++++++++++ .../Factory/WaveTabbarFactory.swift | 5 +++++ 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/TabbarController/FactoryImpl/WaveTabbarFactoryImpl.swift create mode 100644 Poppool/PresentationLayer/Presentation/PresentationInterface/Factory/WaveTabbarFactory.swift diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift index 6c41ec2c..6c9a56a7 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift @@ -78,10 +78,12 @@ final class LoginReactor: Reactor { ) case .moveToHomeScene(let controller): - let homeTabbar = WaveTabBarController() - controller.view.window?.rootViewController = homeTabbar + @Dependency var factory: WaveTabbarFactory + controller.view.window?.rootViewController = factory.make() + case .loadView: break + case .moveToInquiryScene(let controller): @Dependency var factory: FAQFactory controller.navigationController?.pushViewController(factory.make(), animated: true) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/TabbarController/FactoryImpl/WaveTabbarFactoryImpl.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/TabbarController/FactoryImpl/WaveTabbarFactoryImpl.swift new file mode 100644 index 00000000..36cc5a42 --- /dev/null +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/TabbarController/FactoryImpl/WaveTabbarFactoryImpl.swift @@ -0,0 +1,11 @@ +import UIKit + +import PresentationInterface + +public final class WaveTabbarFactoryImpl: WaveTabbarFactory { + public init() { } + + public func make() -> UITabBarController { + return WaveTabBarController() + } +} diff --git a/Poppool/PresentationLayer/Presentation/PresentationInterface/Factory/WaveTabbarFactory.swift b/Poppool/PresentationLayer/Presentation/PresentationInterface/Factory/WaveTabbarFactory.swift new file mode 100644 index 00000000..42d75bd8 --- /dev/null +++ b/Poppool/PresentationLayer/Presentation/PresentationInterface/Factory/WaveTabbarFactory.swift @@ -0,0 +1,5 @@ +import UIKit + +public protocol WaveTabbarFactory { + func make() -> UITabBarController +} From 02807fe819d66e0c758f6c28d2e9b2f56d695471 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Jul 2025 21:46:40 +0900 Subject: [PATCH 16/56] =?UTF-8?q?chore/#160:=20=EB=8D=B0=EB=AA=A8=EC=97=90?= =?UTF-8?q?=20=ED=95=84=EC=9A=94=ED=95=9C=20=EC=97=90=EC=85=8B=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 --- .../icon_login_apple.imageset/Contents.json | 12 ++++++++++++ .../icon_login_apple.imageset/logo.svg | 4 ++++ .../icon_login_kakao.imageset/Contents.json | 12 ++++++++++++ .../icon_login_kakao.imageset/logo.svg | 3 +++ .../image_login_logo.imageset/Contents.json | 12 ++++++++++++ .../image_login_logo.imageset/logo.svg | 4 ++++ 6 files changed, 47 insertions(+) create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/icon_login_apple.imageset/Contents.json create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/icon_login_apple.imageset/logo.svg create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/icon_login_kakao.imageset/Contents.json create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/icon_login_kakao.imageset/logo.svg create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/image_login_logo.imageset/Contents.json create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/image_login_logo.imageset/logo.svg diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/icon_login_apple.imageset/Contents.json b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/icon_login_apple.imageset/Contents.json new file mode 100644 index 00000000..ad42a58d --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/icon_login_apple.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "logo.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/icon_login_apple.imageset/logo.svg b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/icon_login_apple.imageset/logo.svg new file mode 100644 index 00000000..6c6013b4 --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/icon_login_apple.imageset/logo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/icon_login_kakao.imageset/Contents.json b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/icon_login_kakao.imageset/Contents.json new file mode 100644 index 00000000..ad42a58d --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/icon_login_kakao.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "logo.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/icon_login_kakao.imageset/logo.svg b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/icon_login_kakao.imageset/logo.svg new file mode 100644 index 00000000..be2acd8b --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/icon_login_kakao.imageset/logo.svg @@ -0,0 +1,3 @@ + + + diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/image_login_logo.imageset/Contents.json b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/image_login_logo.imageset/Contents.json new file mode 100644 index 00000000..ad42a58d --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/image_login_logo.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "logo.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/image_login_logo.imageset/logo.svg b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/image_login_logo.imageset/logo.svg new file mode 100644 index 00000000..2b64374f --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/Resource/Assets.xcassets/image_login_logo.imageset/logo.svg @@ -0,0 +1,4 @@ + + + + From 84c407a3eb673433e526b416e463b7121072d7ac Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Jul 2025 21:46:56 +0900 Subject: [PATCH 17/56] =?UTF-8?q?feat/#160:=20=EB=8D=B0=EB=AA=A8=EC=97=90?= =?UTF-8?q?=20=ED=95=84=EC=9A=94=ED=95=9C=20=EC=9D=98=EC=A1=B4=EC=84=B1=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 --- .../LoginFeature.xcodeproj/project.pbxproj | 75 +++++++++++++++---- .../LoginFeatureDemo/App/AppDelegate.swift | 20 ++++- .../LoginFeatureDemo/App/SceneDelegate.swift | 4 +- 3 files changed, 81 insertions(+), 18 deletions(-) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/project.pbxproj index df3cf150..e115dff9 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/project.pbxproj @@ -18,10 +18,21 @@ 05A7C1D32E267AF80010F1CD /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7C1D22E267AF80010F1CD /* ReactorKit */; }; 05A7C1D62E267B130010F1CD /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7C1D52E267B130010F1CD /* RxCocoa */; }; 05A7C1D82E267B130010F1CD /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7C1D72E267B130010F1CD /* RxSwift */; }; - 05A7CBB62E277AD20010F1CD /* SnapKit-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7CBB52E277AD20010F1CD /* SnapKit-Dynamic */; }; 05A7CBB92E277AE30010F1CD /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7CBB82E277AE30010F1CD /* Then */; }; - 05A7CBBB2E277AF20010F1CD /* SnapKit-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7CBBA2E277AF20010F1CD /* SnapKit-Dynamic */; }; - 05A7CBBC2E277AF20010F1CD /* SnapKit-Dynamic in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 05A7CBBA2E277AF20010F1CD /* SnapKit-Dynamic */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 05A7CBD32E27D0700010F1CD /* PresentationInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBD22E27D0700010F1CD /* PresentationInterface.framework */; }; + 05A7CBD42E27D0700010F1CD /* PresentationInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBD22E27D0700010F1CD /* PresentationInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05A7CBD82E27D3510010F1CD /* Presentation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBD62E27D3510010F1CD /* Presentation.framework */; }; + 05A7CBD92E27D3510010F1CD /* Presentation.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBD62E27D3510010F1CD /* Presentation.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05A7CBDA2E27D3510010F1CD /* PresentationInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBD72E27D3510010F1CD /* PresentationInterface.framework */; }; + 05A7CBDB2E27D3510010F1CD /* PresentationInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBD72E27D3510010F1CD /* PresentationInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05A7CBDF2E27D44F0010F1CD /* Data.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBDC2E27D44F0010F1CD /* Data.framework */; }; + 05A7CBE02E27D44F0010F1CD /* Data.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBDC2E27D44F0010F1CD /* Data.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05A7CBE12E27D44F0010F1CD /* Domain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBDD2E27D44F0010F1CD /* Domain.framework */; }; + 05A7CBE22E27D44F0010F1CD /* Domain.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBDD2E27D44F0010F1CD /* Domain.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05A7CBE32E27D44F0010F1CD /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBDE2E27D44F0010F1CD /* DomainInterface.framework */; }; + 05A7CBE42E27D44F0010F1CD /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBDE2E27D44F0010F1CD /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05A7CBE62E27D5390010F1CD /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7CBE52E27D5390010F1CD /* SnapKit */; }; + 05A7CBE82E27D5400010F1CD /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7CBE72E27D5400010F1CD /* SnapKit */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -58,7 +69,22 @@ 05A7C1BD2E2679FC0010F1CD /* Infrastructure.framework in Embed Frameworks */, 05A7C1B32E26799A0010F1CD /* LoginFeature.framework in Embed Frameworks */, 05A7C1B72E26799A0010F1CD /* LoginFeatureInterface.framework in Embed Frameworks */, - 05A7CBBC2E277AF20010F1CD /* SnapKit-Dynamic in Embed Frameworks */, + 05A7CBD92E27D3510010F1CD /* Presentation.framework in Embed Frameworks */, + 05A7CBE22E27D44F0010F1CD /* Domain.framework in Embed Frameworks */, + 05A7CBE02E27D44F0010F1CD /* Data.framework in Embed Frameworks */, + 05A7CBE42E27D44F0010F1CD /* DomainInterface.framework in Embed Frameworks */, + 05A7CBDB2E27D3510010F1CD /* PresentationInterface.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; + 05A7CBD52E27D0700010F1CD /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 05A7CBD42E27D0700010F1CD /* PresentationInterface.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -71,6 +97,12 @@ 05A7C1922E26771A0010F1CD /* LoginFeatureDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LoginFeatureDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 05A7C1AE2E2679290010F1CD /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05A7C1BB2E2679FC0010F1CD /* Infrastructure.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Infrastructure.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05A7CBD22E27D0700010F1CD /* PresentationInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PresentationInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05A7CBD62E27D3510010F1CD /* Presentation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Presentation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05A7CBD72E27D3510010F1CD /* PresentationInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PresentationInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05A7CBDC2E27D44F0010F1CD /* Data.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Data.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05A7CBDD2E27D44F0010F1CD /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05A7CBDE2E27D44F0010F1CD /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ @@ -109,9 +141,10 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 05A7CBB62E277AD20010F1CD /* SnapKit-Dynamic in Frameworks */, 05A7CBB92E277AE30010F1CD /* Then in Frameworks */, + 05A7CBD32E27D0700010F1CD /* PresentationInterface.framework in Frameworks */, 05A7C1D62E267B130010F1CD /* RxCocoa in Frameworks */, + 05A7CBE62E27D5390010F1CD /* SnapKit in Frameworks */, 05A7C1D32E267AF80010F1CD /* ReactorKit in Frameworks */, 05A7C1D82E267B130010F1CD /* RxSwift in Frameworks */, 05A7C1BE2E267A120010F1CD /* LoginFeatureInterface.framework in Frameworks */, @@ -133,7 +166,12 @@ 05A7C1BC2E2679FC0010F1CD /* Infrastructure.framework in Frameworks */, 05A7C1B22E26799A0010F1CD /* LoginFeature.framework in Frameworks */, 05A7C1B62E26799A0010F1CD /* LoginFeatureInterface.framework in Frameworks */, - 05A7CBBB2E277AF20010F1CD /* SnapKit-Dynamic in Frameworks */, + 05A7CBD82E27D3510010F1CD /* Presentation.framework in Frameworks */, + 05A7CBE12E27D44F0010F1CD /* Domain.framework in Frameworks */, + 05A7CBDF2E27D44F0010F1CD /* Data.framework in Frameworks */, + 05A7CBE82E27D5400010F1CD /* SnapKit in Frameworks */, + 05A7CBE32E27D44F0010F1CD /* DomainInterface.framework in Frameworks */, + 05A7CBDA2E27D3510010F1CD /* PresentationInterface.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -164,6 +202,12 @@ 05A7C1AD2E2679290010F1CD /* Frameworks */ = { isa = PBXGroup; children = ( + 05A7CBDC2E27D44F0010F1CD /* Data.framework */, + 05A7CBDD2E27D44F0010F1CD /* Domain.framework */, + 05A7CBDE2E27D44F0010F1CD /* DomainInterface.framework */, + 05A7CBD62E27D3510010F1CD /* Presentation.framework */, + 05A7CBD72E27D3510010F1CD /* PresentationInterface.framework */, + 05A7CBD22E27D0700010F1CD /* PresentationInterface.framework */, 05A7C1BB2E2679FC0010F1CD /* Infrastructure.framework */, 05A7C1AE2E2679290010F1CD /* DesignSystem.framework */, ); @@ -198,6 +242,7 @@ 05A7C15B2E26768A0010F1CD /* Sources */, 05A7C15C2E26768A0010F1CD /* Frameworks */, 05A7C15D2E26768A0010F1CD /* Resources */, + 05A7CBD52E27D0700010F1CD /* Embed Frameworks */, ); buildRules = ( ); @@ -212,8 +257,8 @@ 05A7C1D22E267AF80010F1CD /* ReactorKit */, 05A7C1D52E267B130010F1CD /* RxCocoa */, 05A7C1D72E267B130010F1CD /* RxSwift */, - 05A7CBB52E277AD20010F1CD /* SnapKit-Dynamic */, 05A7CBB82E277AE30010F1CD /* Then */, + 05A7CBE52E27D5390010F1CD /* SnapKit */, ); productName = LoginFeature; productReference = 05A7C15F2E26768A0010F1CD /* LoginFeature.framework */; @@ -262,7 +307,7 @@ ); name = LoginFeatureDemo; packageProductDependencies = ( - 05A7CBBA2E277AF20010F1CD /* SnapKit-Dynamic */, + 05A7CBE72E27D5400010F1CD /* SnapKit */, ); productName = LoginFeatureDemo; productReference = 05A7C1922E26771A0010F1CD /* LoginFeatureDemo.app */; @@ -826,20 +871,20 @@ package = 05A7C1D42E267B130010F1CD /* XCRemoteSwiftPackageReference "RxSwift" */; productName = RxSwift; }; - 05A7CBB52E277AD20010F1CD /* SnapKit-Dynamic */ = { - isa = XCSwiftPackageProductDependency; - package = 05A7CBB42E277AD20010F1CD /* XCRemoteSwiftPackageReference "SnapKit" */; - productName = "SnapKit-Dynamic"; - }; 05A7CBB82E277AE30010F1CD /* Then */ = { isa = XCSwiftPackageProductDependency; package = 05A7CBB72E277AE30010F1CD /* XCRemoteSwiftPackageReference "Then" */; productName = Then; }; - 05A7CBBA2E277AF20010F1CD /* SnapKit-Dynamic */ = { + 05A7CBE52E27D5390010F1CD /* SnapKit */ = { + isa = XCSwiftPackageProductDependency; + package = 05A7CBB42E277AD20010F1CD /* XCRemoteSwiftPackageReference "SnapKit" */; + productName = SnapKit; + }; + 05A7CBE72E27D5400010F1CD /* SnapKit */ = { isa = XCSwiftPackageProductDependency; package = 05A7CBB42E277AD20010F1CD /* XCRemoteSwiftPackageReference "SnapKit" */; - productName = "SnapKit-Dynamic"; + productName = SnapKit; }; /* End XCSwiftPackageProductDependency section */ }; diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/AppDelegate.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/AppDelegate.swift index cf86052d..35a41b5b 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/AppDelegate.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/AppDelegate.swift @@ -1,8 +1,13 @@ import UIKit +import Data +import Domain +import DomainInterface import Infrastructure import LoginFeature import LoginFeatureInterface +import Presentation +import PresentationInterface @main class AppDelegate: UIResponder, UIApplicationDelegate { @@ -25,18 +30,31 @@ class AppDelegate: UIResponder, UIApplicationDelegate { extension AppDelegate { private func registerDependencies() { // MARK: Register Service + DIContainer.register(Provider.self) { return ProviderImpl() } // MARK: Resolve service + @Dependency var provider: Provider // MARK: Register repository + DIContainer.register(AuthAPIRepository.self) { return AuthAPIRepositoryImpl(provider: provider) } + DIContainer.register(KakaoLoginRepository.self) { return KakaoLoginRepositoryImpl() } + DIContainer.register(AppleLoginRepository.self) { return AppleLoginRepositoryImpl() } // MARK: Resolve repository + @Dependency var authAPIRepository: AuthAPIRepository + @Dependency var kakaoLoginRepository: KakaoLoginRepository + @Dependency var appleLoginRepository: AppleLoginRepository // MARK: Register UseCase - + DIContainer.register(AuthAPIUseCase.self) { return AuthAPIUseCaseImpl(repository: authAPIRepository) } + DIContainer.register(KakaoLoginUseCase.self) { return KakaoLoginUseCaseImpl(repository: kakaoLoginRepository) } + DIContainer.register(AppleLoginUseCase.self) { return AppleLoginUseCaseImpl(repository: appleLoginRepository) } } private func registerFactory() { DIContainer.register(LoginFactory.self) { return LoginFactoryImpl() } + DIContainer.register(SignUpFactory.self) { return SignUpFactoryImpl() } + DIContainer.register(WaveTabbarFactory.self) { return WaveTabbarFactoryImpl() } + DIContainer.register(FAQFactory.self) { return FAQFactoryImpl() } } } diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/SceneDelegate.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/SceneDelegate.swift index f2890258..94b688cc 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/SceneDelegate.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/SceneDelegate.swift @@ -14,10 +14,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { window = UIWindow(windowScene: windowScene) let navigationController = UINavigationController() - @Dependency var loginFactory: LoginFactory + @Dependency var factory: LoginFactory navigationController.pushViewController( - loginFactory.make(), + factory.make(), animated: false ) From cd392b9a75b52744ed075270e3943a35bc2d40c0 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Jul 2025 21:47:30 +0900 Subject: [PATCH 18/56] =?UTF-8?q?fix/#160:=20=EB=A9=94=EC=9D=B8=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EA=B8=B0=EC=A1=B4=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=9D=B4=EC=82=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoginFeature/Login/LoginView.swift | 2 +- .../Login/LoginViewController.swift | 56 ++++++++++++++++--- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginView.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginView.swift index 501ad81f..11469516 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginView.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginView.swift @@ -79,7 +79,7 @@ private extension LoginView { func setupConstraints() { guestButton.snp.makeConstraints { make in - make.top.equalToSuperview().inset(11) + make.top.equalTo(safeAreaLayoutGuide).inset(11) make.trailing.equalToSuperview().inset(20) } diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift index 4da8d15f..7392b47e 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift @@ -18,16 +18,26 @@ final class LoginViewController: BaseViewController, View { override func loadView() { self.view = mainView } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + if let lastLogin = reactor?.userDefaultService.fetch(key: "lastLogin") { + switch lastLogin { + case "kakao": + mainView.kakaoButton.showToolTip(color: .w100, direction: .pointDown, text: "최근에 이 방법으로 로그인했어요") + case "apple": + mainView.appleButton.showToolTip(color: .w100, direction: .pointUp, text: "최근에 이 방법으로 로그인했어요") + default: + break + } + } + } } // MARK: - Life Cycle extension LoginViewController { override func viewDidLoad() { super.viewDidLoad() - - self.addViews() - self.setupConstraints() - self.configureUI() } } @@ -37,13 +47,43 @@ private extension LoginViewController { func setupConstraints() { } - func configureUI() { - mainView.backgroundColor = .black - } + func configureUI() { } } extension LoginViewController { - func bind(reactor: Reactor) { } + func bind(reactor: Reactor) { + mainView.guestButton.rx.tap + .withUnretained(self) + .map { (owner, _) in + Reactor.Action.guestButtonTapped(controller: owner) + } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + mainView.kakaoButton.rx.tap + .withUnretained(self) + .map { (owner, _) in + Reactor.Action.kakaoButtonTapped(controller: owner) + } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + mainView.inquiryButton.rx.tap + .withUnretained(self) + .map { (owner, _) in + Reactor.Action.inquiryButtonTapped(controller: owner) + } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + mainView.appleButton.rx.tap + .withUnretained(self) + .map { (owner, _) in + Reactor.Action.appleButtonTapped(controller: owner) + } + .bind(to: reactor.action) + .disposed(by: disposeBag) + } } From 74f0afbe015e19a50111fe4b533086f158f7d80f Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Jul 2025 21:48:39 +0900 Subject: [PATCH 19/56] =?UTF-8?q?refactor/#160:=20LoginFeature=20=EA=B5=90?= =?UTF-8?q?=EC=B2=B4=20=EC=A4=80=EB=B9=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DIContainer 의존성 추가 - 프로젝트에 필요한 의존성 추가 - Factory를 이용한 화면 전환으로 변경 --- Poppool/Poppool.xcodeproj/project.pbxproj | 12 ++++++++++++ Poppool/Poppool/Application/AppDelegate.swift | 6 ++++++ .../LoginFeature/Login/Sub/SubLoginReactor.swift | 16 +++++++--------- .../Scene/Splash/SplashController.swift | 10 +++------- 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index 925b6c8e..f2e8f2cf 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -33,6 +33,10 @@ 05734C6B2DCE05550093825D /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05734C6A2DCE05550093825D /* ReactorKit */; }; 05734C6E2DCE05680093825D /* Tabman in Frameworks */ = {isa = PBXBuildFile; productRef = 05734C6D2DCE05680093825D /* Tabman */; }; 05734C712DCE059D0093825D /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = 05734C702DCE059D0093825D /* Then */; }; + 05A7CBEB2E27D5FB0010F1CD /* LoginFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBE92E27D5FB0010F1CD /* LoginFeature.framework */; }; + 05A7CBEC2E27D5FB0010F1CD /* LoginFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBE92E27D5FB0010F1CD /* LoginFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05A7CBED2E27D5FB0010F1CD /* LoginFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBEA2E27D5FB0010F1CD /* LoginFeatureInterface.framework */; }; + 05A7CBEE2E27D5FB0010F1CD /* LoginFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBEA2E27D5FB0010F1CD /* LoginFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05BBA73E2DB75DA60047A061 /* KakaoSDKUser in Frameworks */ = {isa = PBXBuildFile; productRef = 05BBA73D2DB75DA60047A061 /* KakaoSDKUser */; }; 4E15142A2D99480200DFD08F /* NMapsMap in Frameworks */ = {isa = PBXBuildFile; productRef = 4E1514292D99480200DFD08F /* NMapsMap */; }; 4E15142E2D994A3A00DFD08F /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 4E15142D2D994A3A00DFD08F /* KakaoSDKAuth */; }; @@ -52,9 +56,11 @@ 0543C5DF2DF86C830070BB93 /* SearchFeatureInterface.framework in Embed Frameworks */, 0543C5D92DF86C7F0070BB93 /* Presentation.framework in Embed Frameworks */, 0543C5D72DF86C7E0070BB93 /* Infrastructure.framework in Embed Frameworks */, + 05A7CBEE2E27D5FB0010F1CD /* LoginFeatureInterface.framework in Embed Frameworks */, 0543C5DD2DF86C810070BB93 /* SearchFeature.framework in Embed Frameworks */, 0543C5D52DF86C7C0070BB93 /* DomainInterface.framework in Embed Frameworks */, 0543C5D12DF86C790070BB93 /* DesignSystem.framework in Embed Frameworks */, + 05A7CBEC2E27D5FB0010F1CD /* LoginFeature.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -66,6 +72,8 @@ 05734C582DCDFAC20093825D /* SearchFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SearchFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05734C592DCDFAC20093825D /* SearchFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SearchFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05734C5E2DCE04CE0093825D /* PresentationInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PresentationInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05A7CBE92E27D5FB0010F1CD /* LoginFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = LoginFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05A7CBEA2E27D5FB0010F1CD /* LoginFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = LoginFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05BDD3D52DB66E1700C1E192 /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05C1D6072DB53A4900508FFD /* Data.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Data.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05C1D6082DB53A4900508FFD /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -107,6 +115,7 @@ buildActionMask = 2147483647; files = ( 0543C5D02DF86C790070BB93 /* DesignSystem.framework in Frameworks */, + 05A7CBEB2E27D5FB0010F1CD /* LoginFeature.framework in Frameworks */, 05734C662DCE05070093825D /* PanModal in Frameworks */, 0543C5D42DF86C7C0070BB93 /* DomainInterface.framework in Frameworks */, 0543C5D62DF86C7E0070BB93 /* Infrastructure.framework in Frameworks */, @@ -124,6 +133,7 @@ 4EE360FD2D91876300D2441D /* NMapsMap in Frameworks */, 0543C5DA2DF86C800070BB93 /* PresentationInterface.framework in Frameworks */, 05734C682DCE05240093825D /* SnapKit in Frameworks */, + 05A7CBED2E27D5FB0010F1CD /* LoginFeatureInterface.framework in Frameworks */, 0543C5CD2DF86C740070BB93 /* Data.framework in Frameworks */, 05734C632DCE04FA0093825D /* Pageboy in Frameworks */, 4E15142A2D99480200DFD08F /* NMapsMap in Frameworks */, @@ -136,6 +146,8 @@ 05C1D6062DB53A4900508FFD /* Frameworks */ = { isa = PBXGroup; children = ( + 05A7CBE92E27D5FB0010F1CD /* LoginFeature.framework */, + 05A7CBEA2E27D5FB0010F1CD /* LoginFeatureInterface.framework */, 05734C5E2DCE04CE0093825D /* PresentationInterface.framework */, 05734C582DCDFAC20093825D /* SearchFeature.framework */, 05734C592DCDFAC20093825D /* SearchFeatureInterface.framework */, diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index 82d5d5a8..ddee740b 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -9,6 +9,8 @@ import Presentation import PresentationInterface import SearchFeature import SearchFeatureInterface +import LoginFeature +import LoginFeatureInterface import KakaoSDKCommon import NMapsMap @@ -100,5 +102,9 @@ extension AppDelegate { DIContainer.register(DetailFactory.self) { return DetailFactoryImpl() } DIContainer.register(CategorySelectorFactory.self) { return CategorySelectorFactoryImpl() } DIContainer.register(FilterSelectorFactory.self) { return FilterSelectorFactoryImpl() } + DIContainer.register(LoginFactory.self) { return LoginFactoryImpl() } + DIContainer.register(SignUpFactory.self) { return SignUpFactoryImpl() } + DIContainer.register(WaveTabbarFactory.self) { return WaveTabbarFactoryImpl() } + DIContainer.register(FAQFactory.self) { return FAQFactoryImpl() } } } diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginReactor.swift index ce0c960b..316fb996 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginReactor.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginReactor.swift @@ -1,6 +1,7 @@ import DesignSystem import DomainInterface import Infrastructure +import PresentationInterface import ReactorKit import RxCocoa @@ -68,21 +69,18 @@ final class SubLoginReactor: Reactor { func reduce(state: State, mutation: Mutation) -> State { switch mutation { case .moveToSignUpScene(let controller): - let signUpController = SignUpMainController() - signUpController.reactor = SignUpMainReactor( - isFirstResponderCase: false, - authrizationCode: authrizationCode, - signUpAPIUseCase: DIContainer.resolve(SignUpAPIUseCase.self) + @Dependency var factory: SignUpFactory + controller.navigationController?.pushViewController( + factory.make(isFirstResponder: false, authrizationCode: authrizationCode), + animated: true ) - controller.navigationController?.pushViewController(signUpController, animated: true) case .dismissScene(let controller): controller.dismiss(animated: true) case .loadView: break case .moveToInquiryScene(let controller): - let nextController = FAQController() - nextController.reactor = FAQReactor() - controller.navigationController?.pushViewController(nextController, animated: true) + @Dependency var factory: FAQFactory + controller.navigationController?.pushViewController(factory.make(), animated: true) } return state } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift index 006b1446..44258e5c 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift @@ -3,6 +3,7 @@ import UIKit import DesignSystem import DomainInterface import Infrastructure +import LoginFeatureInterface import ReactorKit import RxCocoa @@ -62,13 +63,8 @@ private extension SplashController { owner.rootViewController = navigationController }, onError: { [weak self] _ in guard let self = self else { return } - let loginViewController = LoginController() - loginViewController.reactor = LoginReactor( - authAPIUseCase: authAPIUseCase, - kakaoLoginUseCase: DIContainer.resolve(KakaoLoginUseCase.self), - appleLoginUseCase: DIContainer.resolve(AppleLoginUseCase.self) - ) - let loginNavigationController = UINavigationController(rootViewController: loginViewController) + @Dependency var factory: LoginFactory + let loginNavigationController = UINavigationController(rootViewController: factory.make()) rootViewController = loginNavigationController }) .disposed(by: disposeBag) From 6ba01fc47b27c62ceb21ae692afc97b83bdecf12 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Jul 2025 22:00:10 +0900 Subject: [PATCH 20/56] =?UTF-8?q?refactor/#160:=20=EC=84=9C=EB=B8=8C=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EA=B5=90=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/Poppool/Application/AppDelegate.swift | 1 + .../Factory/SubLoginFactory.swift | 20 +++++++++++++ .../Source/Factory/SubLoginFactory.swift | 5 ++++ .../Scene/Detail/DetailReactor.swift | 28 +++++-------------- .../Scene/MyPage/Main/MyPageReactor.swift | 10 ++----- 5 files changed, 36 insertions(+), 28 deletions(-) create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/SubLoginFactory.swift create mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeatureInterface/Source/Factory/SubLoginFactory.swift diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index ddee740b..d3059eb5 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -103,6 +103,7 @@ extension AppDelegate { DIContainer.register(CategorySelectorFactory.self) { return CategorySelectorFactoryImpl() } DIContainer.register(FilterSelectorFactory.self) { return FilterSelectorFactoryImpl() } DIContainer.register(LoginFactory.self) { return LoginFactoryImpl() } + DIContainer.register(SubLoginFactory.self) { return SubLoginFactoryImpl() } DIContainer.register(SignUpFactory.self) { return SignUpFactoryImpl() } DIContainer.register(WaveTabbarFactory.self) { return WaveTabbarFactoryImpl() } DIContainer.register(FAQFactory.self) { return FAQFactoryImpl() } diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/SubLoginFactory.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/SubLoginFactory.swift new file mode 100644 index 00000000..fc0de40e --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/SubLoginFactory.swift @@ -0,0 +1,20 @@ +import DesignSystem +import DomainInterface +import LoginFeatureInterface +import Infrastructure + +public final class SubLoginFactoryImpl: SubLoginFactory { + + public init() { } + + public func make() -> BaseViewController { + let viewController = SubLoginController() + viewController.reactor = SubLoginReactor( + authAPIUseCase: DIContainer.resolve(AuthAPIUseCase.self), + kakaoLoginUseCase: DIContainer.resolve(KakaoLoginUseCase.self), + appleLoginUseCase: DIContainer.resolve(AppleLoginUseCase.self) + ) + + return viewController + } +} diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeatureInterface/Source/Factory/SubLoginFactory.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeatureInterface/Source/Factory/SubLoginFactory.swift new file mode 100644 index 00000000..d205790b --- /dev/null +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeatureInterface/Source/Factory/SubLoginFactory.swift @@ -0,0 +1,5 @@ +import DesignSystem + +public protocol SubLoginFactory { + func make() -> BaseViewController +} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift index e6626483..cefb9e93 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift @@ -3,6 +3,7 @@ import UIKit import DesignSystem import DomainInterface import Infrastructure +import LoginFeatureInterface import LinkPresentation import ReactorKit @@ -167,13 +168,8 @@ final class DetailReactor: Reactor { ) controller.navigationController?.pushViewController(commentController, animated: true) } else { - let loginController = SubLoginController() - loginController.reactor = SubLoginReactor( - authAPIUseCase: DIContainer.resolve(AuthAPIUseCase.self), - kakaoLoginUseCase: DIContainer.resolve(KakaoLoginUseCase.self), - appleLoginUseCase: DIContainer.resolve(AppleLoginUseCase.self) - ) - let nextController = UINavigationController(rootViewController: loginController) + @Dependency var factory: SubLoginFactory + let nextController = UINavigationController(rootViewController: factory.make()) nextController.modalPresentationStyle = .fullScreen controller.present(nextController, animated: true) } @@ -207,13 +203,8 @@ final class DetailReactor: Reactor { ) controller.navigationController?.pushViewController(nextController, animated: true) } else { - let loginController = SubLoginController() - loginController.reactor = SubLoginReactor( - authAPIUseCase: DIContainer.resolve(AuthAPIUseCase.self), - kakaoLoginUseCase: DIContainer.resolve(KakaoLoginUseCase.self), - appleLoginUseCase: DIContainer.resolve(AppleLoginUseCase.self) - ) - let nextController = UINavigationController(rootViewController: loginController) + @Dependency var factory: SubLoginFactory + let nextController = UINavigationController(rootViewController: factory.make()) nextController.modalPresentationStyle = .fullScreen controller.present(nextController, animated: true) } @@ -246,13 +237,8 @@ final class DetailReactor: Reactor { case .moveToRecentScene(let controller): controller.navigationController?.popViewController(animated: true) case .moveToLoginScene(let controller): - let loginController = SubLoginController() - loginController.reactor = SubLoginReactor( - authAPIUseCase: DIContainer.resolve(AuthAPIUseCase.self), - kakaoLoginUseCase: DIContainer.resolve(KakaoLoginUseCase.self), - appleLoginUseCase: DIContainer.resolve(AppleLoginUseCase.self) - ) - let nextController = UINavigationController(rootViewController: loginController) + @Dependency var factory: SubLoginFactory + let nextController = UINavigationController(rootViewController: factory.make()) nextController.modalPresentationStyle = .fullScreen controller.present(nextController, animated: true) case .moveToImageDetailScene(let controller, let cellRow, let imageRow): diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift index 70fa8dbb..fc6a78b6 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift @@ -2,6 +2,7 @@ import UIKit import DesignSystem import DomainInterface +import LoginFeatureInterface import Infrastructure import ReactorKit @@ -267,13 +268,8 @@ final class MyPageReactor: Reactor { controller.navigationController?.pushViewController(nextController, animated: true) case .moveToLoginScene(let controller): - let nextController = SubLoginController() - nextController.reactor = SubLoginReactor( - authAPIUseCase: DIContainer.resolve(AuthAPIUseCase.self), - kakaoLoginUseCase: DIContainer.resolve(KakaoLoginUseCase.self), - appleLoginUseCase: DIContainer.resolve(AppleLoginUseCase.self) - ) - let navigationController = UINavigationController(rootViewController: nextController) + @Dependency var factory: SubLoginFactory + let navigationController = UINavigationController(rootViewController: factory.make()) navigationController.modalPresentationStyle = .fullScreen controller.present(navigationController, animated: true) case .moveToMyCommentScene(let controller): From 03d30b4b1126654faf0698061faff94b8ba024cb Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Jul 2025 22:03:18 +0900 Subject: [PATCH 21/56] =?UTF-8?q?refactor/#160:=20Presentation=20=EB=AA=A8?= =?UTF-8?q?=EB=93=88=EC=97=90=EC=84=9C=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20Con?= =?UTF-8?q?troller=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Scene/Login/LastLoginView.swift | 216 ------------------ .../Scene/Login/Main/LoginController.swift | 88 ------- .../Scene/Login/Main/LoginReactor.swift | 148 ------------ .../Scene/Login/Main/LoginView.swift | 129 ----------- .../Scene/Login/Sub/SubLoginController.swift | 88 ------- .../Scene/Login/Sub/SubLoginReactor.swift | 144 ------------ .../Scene/Login/Sub/SubLoginView.swift | 130 ----------- 7 files changed, 943 deletions(-) delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/LastLoginView.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginView.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginController.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginView.swift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/LastLoginView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/LastLoginView.swift deleted file mode 100644 index eeebbd5a..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/LastLoginView.swift +++ /dev/null @@ -1,216 +0,0 @@ -// -// LastLoginView.swift -// Poppool -// -// Created by SeoJunYoung on 1/16/25. -// - -import UIKit - -import SnapKit - -final class LastLoginView: UIView { - - /// 방향에 따라 툴팁을 다르게 표시합니다 - enum TipDirection { - case pointUp - case pointDown - } - - /// 툴팁의 색상을 지정하였습니다 - /// 텍스트 컬러 또한 TipColor에 따라 수정됩니다 - enum TipColor { - case blu500 - case w100 - - var color: UIColor { - switch self { - case .blu500: return UIColor.blu500 - case .w100: return UIColor.w100 - } - } - - var textColor: UIColor { - switch self { - case .blu500: return UIColor.w100 - case .w100: return UIColor.blu500 - } - } - } - - // MARK: - Properties - - private let bgView: UIView = { - return UIView() - }() - - private let notificationLabel: UILabel = { - let label = UILabel() - label.font = .korFont(style: .medium, size: 13) - return label - }() - - private var colorType: TipColor { - didSet { - self.setNeedsDisplay() - } - } - - private var midX: CGFloat { - return self.bounds.midX - } - - private var direction: TipDirection - - // MARK: - init - - /// 툴팁 뷰를 생성합니다 - /// - Parameters: - /// - colorType: 툴팁의 색상(UIColor)을 인자로 받습니다 - w100, blu500 - /// - direction: 툴팁의 방향을 인자로 받습니다 - up / down - init(colorType: TipColor, direction: TipDirection, text: String?) { - self.colorType = colorType - self.direction = direction - super.init(frame: .zero) - setupLayer(color: colorType) - notificationLabel.textColor = colorType.textColor - notificationLabel.text = text - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func draw(_ rect: CGRect) { - drawToolTip() - } -} - -extension LastLoginView { - - // MARK: - Methods - - private func setupLayer(color: TipColor) { - self.backgroundColor = .clear - addSubview(bgView) - - bgView.snp.makeConstraints { make in - make.height.equalTo(45) - make.edges.equalToSuperview() - } - - bgView.addSubview(notificationLabel) - switch direction { - case .pointUp: - notificationLabel.snp.makeConstraints { make in - make.leading.trailing.equalToSuperview().inset(16) - make.bottom.equalToSuperview().inset(11) - } - - case .pointDown: - notificationLabel.snp.makeConstraints { make in - make.leading.trailing.equalToSuperview().inset(16) - make.top.equalToSuperview().inset(11) - } - } - } - - /// 툴팁을 방향에 맞춰 그리고 섀도우를 더하는 메서드 - private func drawToolTip() { - switch direction { - case .pointUp: - drawUpPointingToolTip() - addShadow() - - case .pointDown: - drawDownPointingTip() - addShadow() - } - } - - /// 위를 가리키는 툴팁을 만듭니다 - private func drawUpPointingToolTip() { - let textLength = notificationLabel.frame.width - let tip = UIBezierPath() - tip.move(to: CGPoint(x: midX - 8, y: 10)) - tip.addLine(to: CGPoint(x: midX, y: 0)) - tip.addLine(to: CGPoint(x: midX + 8, y: 10)) - tip.close() - - colorType.color.setFill() - tip.fill() - - let message = UIBezierPath( - roundedRect: CGRect( - x: 0, y: 10, - width: textLength + 16 + 16, - height: 35 - ), - cornerRadius: 6 - ) - - colorType.color.setFill() - message.fill() - message.close() - } - - /// 아래를 가리키는 툴팁을 만듭니다 - private func drawDownPointingTip() { - let textLength = notificationLabel.frame.width - - let tip = UIBezierPath() - tip.move(to: CGPoint(x: midX - 8, y: 35)) - tip.addLine(to: CGPoint(x: midX, y: 45)) - tip.addLine(to: CGPoint(x: midX + 8, y: 35)) - tip.close() - - colorType.color.setFill() - tip.fill() - - let message = UIBezierPath( - roundedRect: CGRect( - x: 0, y: 0, - width: textLength + 16 + 16, - height: 35 - ), - cornerRadius: 6 - ) - - colorType.color.setFill() - message.fill() - message.close() - } - - /// 툴팁의 섀도우를 더합니다 - private func addShadow() { - layer.shadowOffset = CGSize(width: 0, height: 5) - layer.shadowColor = UIColor.black.cgColor - layer.shadowOpacity = 0.2 - layer.shadowRadius = 5 - - // 섀도우를 그릴 때 드는 리소스를 줄이기 위해 캐시를 적용하는 방식 -// layer.shadowPath = UIBezierPath(rect: self.bounds).cgPath - layer.shouldRasterize = true - layer.rasterizationScale = UIScreen.main.scale - } -} - -extension UIView { - func showToolTip(color: LastLoginView.TipColor, direction: LastLoginView.TipDirection, text: String? = "최근에 이 방법으로 로그인했어요") { - // 호출하는 컴포넌트 위 또는 아래에 생성되기 위해 superview를 구합니다 - guard let superview = self.superview else { return } - - let toolTip = LastLoginView(colorType: color, direction: direction, text: text) - - superview.addSubview(toolTip) - toolTip.snp.makeConstraints { make in - if direction == .pointDown { - make.bottom.equalTo(self.snp.top).offset(-8) - make.centerX.equalToSuperview() - } else { - make.top.equalTo(self.snp.bottom).offset(8) - make.centerX.equalToSuperview() - } - } - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift deleted file mode 100644 index b24a556f..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift +++ /dev/null @@ -1,88 +0,0 @@ -import UIKit - -import DesignSystem -import Infrastructure - -import ReactorKit -import RxCocoa -import RxSwift -import SnapKit - -final class LoginController: BaseViewController, View { - - typealias Reactor = LoginReactor - - // MARK: - Properties - var disposeBag = DisposeBag() - - private var mainView = LoginView() -} - -// MARK: - Life Cycle -extension LoginController { - override func viewDidLoad() { - super.viewDidLoad() - setUp() - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - if let lastLogin = reactor?.userDefaultService.fetch(key: "lastLogin") { - switch lastLogin { - case "kakao": - mainView.kakaoButton.showToolTip(color: .w100, direction: .pointDown, text: "최근에 이 방법으로 로그인했어요") - case "apple": - mainView.appleButton.showToolTip(color: .w100, direction: .pointUp, text: "최근에 이 방법으로 로그인했어요") - default: - break - } - } - } -} - -// MARK: - SetUp -private extension LoginController { - func setUp() { - view.addSubview(mainView) - mainView.snp.makeConstraints { make in - make.edges.equalTo(view.safeAreaLayoutGuide) - } - } -} - -// MARK: - Methods -extension LoginController { - func bind(reactor: Reactor) { - mainView.guestButton.rx.tap - .withUnretained(self) - .map { (owner, _) in - Reactor.Action.guestButtonTapped(controller: owner) - } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.kakaoButton.rx.tap - .withUnretained(self) - .map { (owner, _) in - Reactor.Action.kakaoButtonTapped(controller: owner) - } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.inquiryButton.rx.tap - .withUnretained(self) - .map { (owner, _) in - Reactor.Action.inquiryButtonTapped(controller: owner) - } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.appleButton.rx.tap - .withUnretained(self) - .map { (owner, _) in - Reactor.Action.appleButtonTapped(controller: owner) - } - .bind(to: reactor.action) - .disposed(by: disposeBag) - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift deleted file mode 100644 index 56fcff23..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift +++ /dev/null @@ -1,148 +0,0 @@ -import DesignSystem -import DomainInterface -import Infrastructure - -import ReactorKit -import RxCocoa -import RxSwift - -final class LoginReactor: Reactor { - - // MARK: - Reactor - enum Action { - case kakaoButtonTapped(controller: BaseViewController) - case appleButtonTapped(controller: BaseViewController) - case guestButtonTapped(controller: BaseViewController) - case inquiryButtonTapped(controller: BaseViewController) - } - - enum Mutation { - case moveToSignUpScene(controller: BaseViewController) - case moveToHomeScene(controller: BaseViewController) - case loadView - case moveToInquiryScene(controller: BaseViewController) - } - - struct State { - } - - // MARK: - properties - - var initialState: State - var disposeBag = DisposeBag() - - private var authrizationCode: String? - - private let authAPIUseCase: AuthAPIUseCase - private let kakaoLoginUseCase: KakaoLoginUseCase - private let appleLoginUseCase: AppleLoginUseCase - - @Dependency private var keyChainService: KeyChainService - let userDefaultService = UserDefaultService() - - // MARK: - init - init( - authAPIUseCase: AuthAPIUseCase, - kakaoLoginUseCase: KakaoLoginUseCase, - appleLoginUseCase: AppleLoginUseCase - ) { - self.authAPIUseCase = authAPIUseCase - self.kakaoLoginUseCase = kakaoLoginUseCase - self.appleLoginUseCase = appleLoginUseCase - self.initialState = State() - } - - // MARK: - Reactor Methods - func mutate(action: Action) -> Observable { - switch action { - case .kakaoButtonTapped(let controller): - return loginWithKakao(controller: controller) - case .appleButtonTapped(let controller): - return loginWithApple(controller: controller) - case .guestButtonTapped(let controller): - _ = keyChainService.deleteToken(type: .accessToken) - _ = keyChainService.deleteToken(type: .refreshToken) - return Observable.just(.moveToHomeScene(controller: controller)) - case .inquiryButtonTapped(let controller): - return Observable.just(.moveToInquiryScene(controller: controller)) - } - } - - func reduce(state: State, mutation: Mutation) -> State { - switch mutation { - case .moveToSignUpScene(let controller): - let signUpController = SignUpMainController() - signUpController.reactor = SignUpMainReactor( - isFirstResponderCase: true, - authrizationCode: authrizationCode, - signUpAPIUseCase: DIContainer.resolve(SignUpAPIUseCase.self) - ) - controller.navigationController?.pushViewController(signUpController, animated: true) - case .moveToHomeScene(let controller): - let homeTabbar = WaveTabBarController() - controller.view.window?.rootViewController = homeTabbar - case .loadView: - break - case .moveToInquiryScene(let controller): - let nextController = FAQController() - nextController.reactor = FAQReactor() - controller.navigationController?.pushViewController(nextController, animated: true) - } - return state - } - - func loginWithKakao(controller: BaseViewController) -> Observable { - return kakaoLoginUseCase.fetchUserCredential() - .withUnretained(self) - .flatMap { owner, response in - return owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "kakao") - } - .withUnretained(self) - .map { [weak controller] (owner, loginResponse) in - guard let controller = controller else { return .loadView } - owner.userDefaultService.save(key: "userID", value: loginResponse.userId) - owner.userDefaultService.save(key: "socialType", value: loginResponse.socialType) - let accessTokenResult = owner.keyChainService.saveToken(type: .accessToken, value: loginResponse.accessToken) - let refreshTokenResult = owner.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) - switch accessTokenResult { - case .success: - owner.userDefaultService.save(key: "lastLogin", value: "kakao") - if loginResponse.isRegisteredUser { - return .moveToHomeScene(controller: controller) - } else { - return .moveToSignUpScene(controller: controller) - } - case .failure: - return .loadView - } - } - } - - func loginWithApple(controller: BaseViewController) -> Observable { - return appleLoginUseCase.fetchUserCredential() - .withUnretained(self) - .flatMap { owner, response in - owner.authrizationCode = response.authorizationCode - return owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "apple") - } - .withUnretained(self) - .map({ [weak controller] (owner, loginResponse) in - guard let controller = controller else { return .loadView } - owner.userDefaultService.save(key: "userID", value: loginResponse.userId) - owner.userDefaultService.save(key: "socialType", value: loginResponse.socialType) - let accessTokenResult = owner.keyChainService.saveToken(type: .accessToken, value: loginResponse.accessToken) - let refreshTokenResult = owner.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) - switch accessTokenResult { - case .success: - owner.userDefaultService.save(key: "lastLogin", value: "apple") - if loginResponse.isRegisteredUser { - return .moveToHomeScene(controller: controller) - } else { - return .moveToSignUpScene(controller: controller) - } - case .failure: - return .loadView - } - }) - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginView.swift deleted file mode 100644 index 62d5b8ad..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginView.swift +++ /dev/null @@ -1,129 +0,0 @@ -import UIKit - -import DesignSystem - -import SnapKit - -final class LoginView: UIView { - - // MARK: - Components - let guestButton: UIButton = { - let button = UIButton(type: .system) - button.setTitle("둘러보기", for: .normal) - button.titleLabel?.font = .korFont(style: .regular, size: 14) - button.setTitleColor(.g1000, for: .normal) - return button - }() - - private let logoImageView: UIImageView = { - let view = UIImageView() - view.image = UIImage(named: "image_login_logo") - view.contentMode = .scaleAspectFit - return view - }() - - private let titleLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 16, text: "간편하게 SNS 로그인하고\n팝풀 서비스를 이용해보세요") - label.numberOfLines = 0 - label.textAlignment = .center - return label - }() - - let kakaoButton: PPButton = { - return PPButton(style: .kakao, text: "카카오톡으로 로그인") - }() - - private let kakaoImageView: UIImageView = { - let view = UIImageView() - view.image = UIImage(named: "icon_login_kakao") - return view - }() - - let appleButton: PPButton = { - return PPButton(style: .apple, text: "Apple로 로그인") - }() - - private let appleImageView: UIImageView = { - let view = UIImageView() - view.image = UIImage(named: "icon_login_apple") - return view - }() - - let inquiryButton: UIButton = { - let button = UIButton(type: .system) - button.setTitle("로그인이 어려우신가요?", for: .normal) - button.titleLabel?.font = .korFont(style: .regular, size: 12) - button.setTitleColor(.g1000, for: .normal) - return button - }() - - // MARK: - init - init() { - super.init(frame: .zero) - setUpConstraints() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp -private extension LoginView { - - func setUpConstraints() { - self.addSubview(guestButton) - guestButton.snp.makeConstraints { make in - make.top.equalToSuperview().inset(11) - make.trailing.equalToSuperview().inset(20) - } - - self.addSubview(logoImageView) - logoImageView.snp.makeConstraints { make in - make.height.equalTo(90) - make.width.equalTo(70) - make.top.equalTo(guestButton.snp.bottom).offset(75) - make.centerX.equalToSuperview() - } - - self.addSubview(titleLabel) - titleLabel.snp.makeConstraints { make in - make.centerX.equalToSuperview() - make.top.equalTo(logoImageView.snp.bottom).offset(28) - } - - self.addSubview(kakaoButton) - kakaoButton.snp.makeConstraints { make in - make.top.equalTo(titleLabel.snp.bottom).offset(156) - make.leading.trailing.equalToSuperview().inset(20) - make.height.equalTo(50) - } - - kakaoButton.addSubview(kakaoImageView) - kakaoImageView.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.leading.equalToSuperview().inset(20) - make.size.equalTo(22) - } - - self.addSubview(appleButton) - appleButton.snp.makeConstraints { make in - make.top.equalTo(kakaoButton.snp.bottom).offset(16) - make.leading.trailing.equalToSuperview().inset(20) - make.height.equalTo(50) - } - - appleButton.addSubview(appleImageView) - appleImageView.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.leading.equalToSuperview().inset(20) - make.size.equalTo(22) - } - - self.addSubview(inquiryButton) - inquiryButton.snp.makeConstraints { make in - make.bottom.equalToSuperview().inset(56) - make.centerX.equalToSuperview() - } - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginController.swift deleted file mode 100644 index f1d42ee9..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginController.swift +++ /dev/null @@ -1,88 +0,0 @@ -import UIKit - -import DesignSystem - -import ReactorKit -import RxCocoa -import RxSwift -import SnapKit - -final class SubLoginController: BaseViewController, View { - - typealias Reactor = SubLoginReactor - - // MARK: - Properties - var disposeBag = DisposeBag() - - private var mainView = SubLoginView() -} - -// MARK: - Life Cycle -extension SubLoginController { - override func viewDidLoad() { - super.viewDidLoad() - setUp() - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - if let lastLogin = reactor?.userDefaultService.fetch(key: "lastLogin") { - switch lastLogin { - case "kakao": - mainView.kakaoButton.showToolTip(color: .w100, direction: .pointDown, text: "최근에 이 방법으로 로그인했어요") - case "apple": - mainView.appleButton.showToolTip(color: .w100, direction: .pointUp, text: "최근에 이 방법으로 로그인했어요") - default: - break - } - } - } -} - -// MARK: - SetUp -private extension SubLoginController { - func setUp() { - view.backgroundColor = .g50 - view.addSubview(mainView) - mainView.snp.makeConstraints { make in - make.edges.equalTo(view.safeAreaLayoutGuide) - } - } -} - -// MARK: - Methods -extension SubLoginController { - func bind(reactor: Reactor) { - mainView.xmarkButton.rx.tap - .withUnretained(self) - .map { (owner, _) in - Reactor.Action.xmarkButtonTapped(controller: owner) - } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.kakaoButton.rx.tap - .withUnretained(self) - .map { (owner, _) in - Reactor.Action.kakaoButtonTapped(controller: owner) - } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.inquiryButton.rx.tap - .withUnretained(self) - .map { (owner, _) in - Reactor.Action.inquiryButtonTapped(controller: owner) - } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.appleButton.rx.tap - .withUnretained(self) - .map { (owner, _) in - Reactor.Action.appleButtonTapped(controller: owner) - } - .bind(to: reactor.action) - .disposed(by: disposeBag) - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift deleted file mode 100644 index ce0c960b..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift +++ /dev/null @@ -1,144 +0,0 @@ -import DesignSystem -import DomainInterface -import Infrastructure - -import ReactorKit -import RxCocoa -import RxSwift - -final class SubLoginReactor: Reactor { - - // MARK: - Reactor - enum Action { - case kakaoButtonTapped(controller: BaseViewController) - case appleButtonTapped(controller: BaseViewController) - case xmarkButtonTapped(controller: BaseViewController) - case inquiryButtonTapped(controller: BaseViewController) - } - - enum Mutation { - case moveToSignUpScene(controller: BaseViewController) - case dismissScene(controller: BaseViewController) - case loadView - case moveToInquiryScene(controller: BaseViewController) - } - - struct State { - } - - // MARK: - properties - - var initialState: State - var disposeBag = DisposeBag() - - private var authrizationCode: String? - - private let authAPIUseCase: AuthAPIUseCase - private let kakaoLoginUseCase: KakaoLoginUseCase - private let appleLoginUseCase: AppleLoginUseCase - @Dependency private var keyChainService: KeyChainService - let userDefaultService = UserDefaultService() - - // MARK: - init - init( - authAPIUseCase: AuthAPIUseCase, - kakaoLoginUseCase: KakaoLoginUseCase, - appleLoginUseCase: AppleLoginUseCase - ) { - self.authAPIUseCase = authAPIUseCase - self.kakaoLoginUseCase = kakaoLoginUseCase - self.appleLoginUseCase = appleLoginUseCase - self.initialState = State() - } - - // MARK: - Reactor Methods - func mutate(action: Action) -> Observable { - switch action { - case .kakaoButtonTapped(let controller): - return loginWithKakao(controller: controller) - case .appleButtonTapped(let controller): - return loginWithApple(controller: controller) - case .xmarkButtonTapped(let controller): - return Observable.just(.dismissScene(controller: controller)) - case .inquiryButtonTapped(let controller): - return Observable.just(.moveToInquiryScene(controller: controller)) - } - } - - func reduce(state: State, mutation: Mutation) -> State { - switch mutation { - case .moveToSignUpScene(let controller): - let signUpController = SignUpMainController() - signUpController.reactor = SignUpMainReactor( - isFirstResponderCase: false, - authrizationCode: authrizationCode, - signUpAPIUseCase: DIContainer.resolve(SignUpAPIUseCase.self) - ) - controller.navigationController?.pushViewController(signUpController, animated: true) - case .dismissScene(let controller): - controller.dismiss(animated: true) - case .loadView: - break - case .moveToInquiryScene(let controller): - let nextController = FAQController() - nextController.reactor = FAQReactor() - controller.navigationController?.pushViewController(nextController, animated: true) - } - return state - } - - func loginWithKakao(controller: BaseViewController) -> Observable { - return kakaoLoginUseCase.fetchUserCredential() - .withUnretained(self) - .flatMap { owner, response in - owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "kakao") - } - .withUnretained(self) - .map { [weak controller] (owner, loginResponse) in - guard let controller = controller else { return .loadView } - owner.userDefaultService.save(key: "userID", value: loginResponse.userId) - owner.userDefaultService.save(key: "socialType", value: loginResponse.socialType) - let accessTokenResult = owner.keyChainService.saveToken(type: .accessToken, value: loginResponse.accessToken) - let refreshTokenResult = owner.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) - switch accessTokenResult { - case .success: - owner.userDefaultService.save(key: "lastLogin", value: "kakao") - if loginResponse.isRegisteredUser { - return .dismissScene(controller: controller) - } else { - return .moveToSignUpScene(controller: controller) - } - case .failure: - return .loadView - } - } - } - - func loginWithApple(controller: BaseViewController) -> Observable { - return appleLoginUseCase.fetchUserCredential() - .withUnretained(self) - .flatMap { owner, response in - owner.authrizationCode = response.authorizationCode - return owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "apple") - } - .withUnretained(self) - .map { [weak controller] (owner, loginResponse) in - guard let controller = controller else { return .loadView } - owner.userDefaultService.save(key: "userID", value: loginResponse.userId) - owner.userDefaultService.save(key: "socialType", value: loginResponse.socialType) - let accessTokenResult = owner.keyChainService.saveToken(type: .accessToken, value: loginResponse.accessToken) - let refreshTokenResult = owner.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) - switch accessTokenResult { - case .success: - owner.userDefaultService.save(key: "lastLogin", value: "apple") - if loginResponse.isRegisteredUser { - return .dismissScene(controller: controller) - } else { - return .moveToSignUpScene(controller: controller) - } - case .failure: - return .loadView - } - } - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginView.swift deleted file mode 100644 index cea96869..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginView.swift +++ /dev/null @@ -1,130 +0,0 @@ -import UIKit - -import DesignSystem - -import SnapKit - -final class SubLoginView: UIView { - - // MARK: - Components - let xmarkButton: UIButton = { - let button = UIButton(type: .system) - button.setImage(UIImage(named: "icon_xmark"), for: .normal) - button.tintColor = .g1000 - return button - }() - - private let logoImageView: UIImageView = { - let view = UIImageView() - view.image = UIImage(named: "image_login_logo") - view.contentMode = .scaleAspectFit - return view - }() - - private let titleLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 16, text: "간편하게 SNS 로그인하고\n공감가는 코멘트에 반응해볼까요?\n다른 코멘트를 확인해볼까요?") - label.setLineHeightText(text: "간편하게 SNS 로그인하고\n공감가는 코멘트에 반응해볼까요?\n다른 코멘트를 확인해볼까요?", font: .korFont(style: .bold, size: 16), lineHeight: 1.3) - label.numberOfLines = 0 - label.textAlignment = .center - return label - }() - - let kakaoButton: PPButton = { - return PPButton(style: .kakao, text: "카카오톡으로 로그인") - }() - - private let kakaoImageView: UIImageView = { - let view = UIImageView() - view.image = UIImage(named: "icon_login_kakao") - return view - }() - - let appleButton: PPButton = { - return PPButton(style: .apple, text: "Apple로 로그인") - }() - - private let appleImageView: UIImageView = { - let view = UIImageView() - view.image = UIImage(named: "icon_login_apple") - return view - }() - - let inquiryButton: UIButton = { - let button = UIButton(type: .system) - button.setTitle("로그인이 어려우신가요?", for: .normal) - button.titleLabel?.font = .korFont(style: .regular, size: 12) - button.setTitleColor(.g1000, for: .normal) - return button - }() - - // MARK: - init - init() { - super.init(frame: .zero) - setUpConstraints() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp -private extension SubLoginView { - - func setUpConstraints() { - self.addSubview(xmarkButton) - xmarkButton.snp.makeConstraints { make in - make.top.equalToSuperview().inset(11) - make.trailing.equalToSuperview().inset(20) - make.size.equalTo(32) - } - - self.addSubview(logoImageView) - logoImageView.snp.makeConstraints { make in - make.height.equalTo(90) - make.width.equalTo(70) - make.top.equalTo(xmarkButton.snp.bottom).offset(75) - make.centerX.equalToSuperview() - } - - self.addSubview(titleLabel) - titleLabel.snp.makeConstraints { make in - make.centerX.equalToSuperview() - make.top.equalTo(logoImageView.snp.bottom).offset(28) - } - - self.addSubview(kakaoButton) - kakaoButton.snp.makeConstraints { make in - make.top.equalTo(titleLabel.snp.bottom).offset(156) - make.leading.trailing.equalToSuperview().inset(20) - make.height.equalTo(50) - } - - kakaoButton.addSubview(kakaoImageView) - kakaoImageView.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.leading.equalToSuperview().inset(20) - make.size.equalTo(22) - } - - self.addSubview(appleButton) - appleButton.snp.makeConstraints { make in - make.top.equalTo(kakaoButton.snp.bottom).offset(16) - make.leading.trailing.equalToSuperview().inset(20) - make.height.equalTo(50) - } - - appleButton.addSubview(appleImageView) - appleImageView.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.leading.equalToSuperview().inset(20) - make.size.equalTo(22) - } - - self.addSubview(inquiryButton) - inquiryButton.snp.makeConstraints { make in - make.bottom.equalToSuperview().inset(56) - make.centerX.equalToSuperview() - } - } -} From 9395200769c097a7821649f8080d6c82f15936f5 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 17 Jul 2025 18:31:05 +0900 Subject: [PATCH 22/56] =?UTF-8?q?fix/#160:=20=EC=B5=9C=EA=B7=BC=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=ED=88=B4=ED=8C=81=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoginFeature/Factory/LoginFactory.swift | 11 ++- .../LoginFeature/Login/LastLoginView.swift | 10 ++- .../LoginFeature/Login/LoginReactor.swift | 2 +- .../LoginFeature/Login/LoginView.swift | 5 +- .../Login/LoginViewController.swift | 28 ++---- .../Login/Main/LoginController.swift | 88 ------------------- .../Login/Sub/SubLoginController.swift | 4 +- 7 files changed, 29 insertions(+), 119 deletions(-) delete mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Main/LoginController.swift diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/LoginFactory.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/LoginFactory.swift index 5f381fba..15b7cc8f 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/LoginFactory.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/LoginFactory.swift @@ -1,13 +1,22 @@ import UIKit import DesignSystem +import DomainInterface import LoginFeatureInterface +import Infrastructure public final class LoginFactoryImpl: LoginFactory { public init() { } public func make() -> BaseViewController { - return LoginViewController() + let viewController = LoginViewController() + viewController.reactor = LoginReactor( + authAPIUseCase: DIContainer.resolve(AuthAPIUseCase.self), + kakaoLoginUseCase: DIContainer.resolve(KakaoLoginUseCase.self), + appleLoginUseCase: DIContainer.resolve(AppleLoginUseCase.self) + ) + + return viewController } } diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LastLoginView.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LastLoginView.swift index 102c5fff..7a43ada4 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LastLoginView.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LastLoginView.swift @@ -182,18 +182,24 @@ extension LastLoginView { layer.shadowRadius = 5 // 섀도우를 그릴 때 드는 리소스를 줄이기 위해 캐시를 적용하는 방식 -// layer.shadowPath = UIBezierPath(rect: self.bounds).cgPath + // layer.shadowPath = UIBezierPath(rect: self.bounds).cgPath layer.shouldRasterize = true layer.rasterizationScale = UIScreen.main.scale } } extension UIView { - func showToolTip(color: LastLoginView.TipColor, direction: LastLoginView.TipDirection, text: String? = "최근에 이 방법으로 로그인했어요") { + func showToolTip( + color: LastLoginView.TipColor, + direction: LastLoginView.TipDirection, + text: String? = "최근에 이 방법으로 로그인했어요" + ) { // 호출하는 컴포넌트 위 또는 아래에 생성되기 위해 superview를 구합니다 guard let superview = self.superview else { return } let toolTip = LastLoginView(colorType: color, direction: direction, text: text) + let beforeToolTip = superview.subviews.filter { $0 is LastLoginView } + beforeToolTip.forEach { $0.removeFromSuperview() } superview.addSubview(toolTip) toolTip.snp.makeConstraints { make in diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift index 6c9a56a7..741cc4d3 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift @@ -83,7 +83,7 @@ final class LoginReactor: Reactor { case .loadView: break - + case .moveToInquiryScene(let controller): @Dependency var factory: FAQFactory controller.navigationController?.pushViewController(factory.make(), animated: true) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginView.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginView.swift index 11469516..7dc9bb4d 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginView.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginView.swift @@ -52,9 +52,8 @@ final class LoginView: UIView { self.addViews() self.setupConstraints() - self.configureUI() } - + required init?(coder: NSCoder) { fatalError("\(#file), \(#function) Error") } @@ -124,6 +123,4 @@ private extension LoginView { make.centerX.equalToSuperview() } } - - func configureUI() { } } diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift index 7392b47e..4e48413f 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift @@ -2,9 +2,9 @@ import UIKit import DesignSystem import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit final class LoginViewController: BaseViewController, View { @@ -18,15 +18,19 @@ final class LoginViewController: BaseViewController, View { override func loadView() { self.view = mainView } +} + +// MARK: - Life Cycle +extension LoginViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) if let lastLogin = reactor?.userDefaultService.fetch(key: "lastLogin") { switch lastLogin { case "kakao": - mainView.kakaoButton.showToolTip(color: .w100, direction: .pointDown, text: "최근에 이 방법으로 로그인했어요") + mainView.kakaoButton.showToolTip(color: .w100, direction: .pointDown) case "apple": - mainView.appleButton.showToolTip(color: .w100, direction: .pointUp, text: "최근에 이 방법으로 로그인했어요") + mainView.appleButton.showToolTip(color: .w100, direction: .pointUp) default: break } @@ -34,22 +38,6 @@ final class LoginViewController: BaseViewController, View { } } -// MARK: - Life Cycle -extension LoginViewController { - override func viewDidLoad() { - super.viewDidLoad() - } -} - -// MARK: - SetUp -private extension LoginViewController { - func addViews() { } - - func setupConstraints() { } - - func configureUI() { } -} - extension LoginViewController { func bind(reactor: Reactor) { mainView.guestButton.rx.tap @@ -85,5 +73,3 @@ extension LoginViewController { .disposed(by: disposeBag) } } - - diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Main/LoginController.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Main/LoginController.swift deleted file mode 100644 index b24a556f..00000000 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Main/LoginController.swift +++ /dev/null @@ -1,88 +0,0 @@ -import UIKit - -import DesignSystem -import Infrastructure - -import ReactorKit -import RxCocoa -import RxSwift -import SnapKit - -final class LoginController: BaseViewController, View { - - typealias Reactor = LoginReactor - - // MARK: - Properties - var disposeBag = DisposeBag() - - private var mainView = LoginView() -} - -// MARK: - Life Cycle -extension LoginController { - override func viewDidLoad() { - super.viewDidLoad() - setUp() - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - if let lastLogin = reactor?.userDefaultService.fetch(key: "lastLogin") { - switch lastLogin { - case "kakao": - mainView.kakaoButton.showToolTip(color: .w100, direction: .pointDown, text: "최근에 이 방법으로 로그인했어요") - case "apple": - mainView.appleButton.showToolTip(color: .w100, direction: .pointUp, text: "최근에 이 방법으로 로그인했어요") - default: - break - } - } - } -} - -// MARK: - SetUp -private extension LoginController { - func setUp() { - view.addSubview(mainView) - mainView.snp.makeConstraints { make in - make.edges.equalTo(view.safeAreaLayoutGuide) - } - } -} - -// MARK: - Methods -extension LoginController { - func bind(reactor: Reactor) { - mainView.guestButton.rx.tap - .withUnretained(self) - .map { (owner, _) in - Reactor.Action.guestButtonTapped(controller: owner) - } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.kakaoButton.rx.tap - .withUnretained(self) - .map { (owner, _) in - Reactor.Action.kakaoButtonTapped(controller: owner) - } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.inquiryButton.rx.tap - .withUnretained(self) - .map { (owner, _) in - Reactor.Action.inquiryButtonTapped(controller: owner) - } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.appleButton.rx.tap - .withUnretained(self) - .map { (owner, _) in - Reactor.Action.appleButtonTapped(controller: owner) - } - .bind(to: reactor.action) - .disposed(by: disposeBag) - } -} diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginController.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginController.swift index f1d42ee9..1b989757 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginController.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginController.swift @@ -29,9 +29,9 @@ extension SubLoginController { if let lastLogin = reactor?.userDefaultService.fetch(key: "lastLogin") { switch lastLogin { case "kakao": - mainView.kakaoButton.showToolTip(color: .w100, direction: .pointDown, text: "최근에 이 방법으로 로그인했어요") + mainView.kakaoButton.showToolTip(color: .w100, direction: .pointDown) case "apple": - mainView.appleButton.showToolTip(color: .w100, direction: .pointUp, text: "최근에 이 방법으로 로그인했어요") + mainView.appleButton.showToolTip(color: .w100, direction: .pointUp) default: break } From a14f3535f47c886f00d60dc579670528a9c90a4f Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 17 Jul 2025 18:33:06 +0900 Subject: [PATCH 23/56] =?UTF-8?q?feat/#160:=20=ED=82=A4=EC=B2=B4=EC=9D=B8?= =?UTF-8?q?=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EA=B2=B0=EA=B3=BC=20=EB=AF=B8?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9D=84=20=EC=9C=84=ED=95=9C=20discardableR?= =?UTF-8?q?esult=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Infrastructure/Infrastructure/Service/KeyChainService.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift index 822263e7..82e82cd7 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift @@ -60,6 +60,7 @@ public final class KeyChainService { /// - Parameter type: 저장하려는 토큰의 타입 (`accessToken` 또는 `refreshToken`) /// - Parameter value: 저장할 토큰의 값 /// - Returns: 완료 시 `Completable` + @discardableResult public func saveToken(type: TokenType, value: String) -> Result { // allowLossyConversion은 인코딩 과정에서 손실이 되는 것을 허용할 것인지 설정 guard let convertValue = value.data(using: .utf8, allowLossyConversion: false) else { @@ -95,6 +96,7 @@ public final class KeyChainService { /// KeyChain에서 특정 타입의 토큰을 삭제하는 메서드 /// - Parameter type: 삭제하려는 토큰의 타입 (`accessToken` 또는 `refreshToken`) /// - Returns: 완료 시 `Completable` + @discardableResult public func deleteToken(type: TokenType) -> Result { // 1. query 작성 let keyChainQuery: NSDictionary = [ From d60bd2df99f7cf0e4961999fc05524140545e64f Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 17 Jul 2025 18:49:09 +0900 Subject: [PATCH 24/56] =?UTF-8?q?test/#160:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=20=EB=8D=B0=EB=AA=A8=EB=A5=BC=20=EC=9C=84=ED=95=9C=20Presentat?= =?UTF-8?q?ionTesting=20=EA=B5=AC=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 화면 전환에 필요한 화면을 별도의 Mock 객체로 대체 --- .../xcshareddata/swiftpm/Package.resolved | 10 +- .../LoginFeature.xcodeproj/project.pbxproj | 99 ++++++++++- .../LoginFeatureDemo/App/AppDelegate.swift | 13 +- .../Presentation.xcodeproj/project.pbxproj | 165 +++++++++++++++++- .../Factory/FAQFactoryMock.swift | 11 ++ .../Factory/SignUpFactoryMock.swift | 11 ++ .../Factory/WaveTabbarFactoryMock.swift | 12 ++ 7 files changed, 307 insertions(+), 14 deletions(-) create mode 100644 Poppool/PresentationLayer/Presentation/PresentationTesting/Factory/FAQFactoryMock.swift create mode 100644 Poppool/PresentationLayer/Presentation/PresentationTesting/Factory/SignUpFactoryMock.swift create mode 100644 Poppool/PresentationLayer/Presentation/PresentationTesting/Factory/WaveTabbarFactoryMock.swift diff --git a/Poppool/Poppool.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Poppool/Poppool.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7bcd7a79..eedcb23b 100644 --- a/Poppool/Poppool.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Poppool/Poppool.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "e3308f4d8d004a111784c1f182de9af75f8c85dd7807e55541802130c4c227fb", + "originHash" : "a7f3b61af0b76d9c1de1a59a03a575ed7c287144c0d5f2019526285cd427e6a7", "pins" : [ { "identity" : "alamofire", @@ -24,8 +24,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/kakao/kakao-ios-sdk.git", "state" : { - "revision" : "2c780b70d4197be3f72deee2fd82ea3e3b767007", - "version" : "2.24.1" + "revision" : "394ccfc67b206894f55dcaf2dd15e06bf451c4d3", + "version" : "2.24.5" } }, { @@ -123,8 +123,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/navermaps/SPM-NMapsMap", "state" : { - "revision" : "ad89e53fdfec3b8d8994280fb0414b5a7b1c3e8e", - "version" : "3.21.0" + "revision" : "13a6d280a57c4ebab8320e2d5bf3ce89adacf95e", + "version" : "3.22.0" } }, { diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/project.pbxproj index e115dff9..959b38e5 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 055C24F02E28FD9800AD389C /* PresentationTesting.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 055C24EF2E28FD9800AD389C /* PresentationTesting.framework */; }; + 055C24F12E28FD9800AD389C /* PresentationTesting.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 055C24EF2E28FD9800AD389C /* PresentationTesting.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05A7C1AF2E2679290010F1CD /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7C1AE2E2679290010F1CD /* DesignSystem.framework */; }; 05A7C1B22E26799A0010F1CD /* LoginFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7C15F2E26768A0010F1CD /* LoginFeature.framework */; }; 05A7C1B32E26799A0010F1CD /* LoginFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7C15F2E26768A0010F1CD /* LoginFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -21,8 +23,6 @@ 05A7CBB92E277AE30010F1CD /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7CBB82E277AE30010F1CD /* Then */; }; 05A7CBD32E27D0700010F1CD /* PresentationInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBD22E27D0700010F1CD /* PresentationInterface.framework */; }; 05A7CBD42E27D0700010F1CD /* PresentationInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBD22E27D0700010F1CD /* PresentationInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 05A7CBD82E27D3510010F1CD /* Presentation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBD62E27D3510010F1CD /* Presentation.framework */; }; - 05A7CBD92E27D3510010F1CD /* Presentation.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBD62E27D3510010F1CD /* Presentation.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05A7CBDA2E27D3510010F1CD /* PresentationInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBD72E27D3510010F1CD /* PresentationInterface.framework */; }; 05A7CBDB2E27D3510010F1CD /* PresentationInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBD72E27D3510010F1CD /* PresentationInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05A7CBDF2E27D44F0010F1CD /* Data.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBDC2E27D44F0010F1CD /* Data.framework */; }; @@ -33,6 +33,15 @@ 05A7CBE42E27D44F0010F1CD /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBDE2E27D44F0010F1CD /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05A7CBE62E27D5390010F1CD /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7CBE52E27D5390010F1CD /* SnapKit */; }; 05A7CBE82E27D5400010F1CD /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7CBE72E27D5400010F1CD /* SnapKit */; }; + 05A7CBF72E27DBB30010F1CD /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7C1AE2E2679290010F1CD /* DesignSystem.framework */; }; + 05A7CBF82E27DBB30010F1CD /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7C1AE2E2679290010F1CD /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05A7CBFA2E27DBDE0010F1CD /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7CBF92E27DBDE0010F1CD /* ReactorKit */; }; + 05A7CBFC2E27DBDE0010F1CD /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7CBFB2E27DBDE0010F1CD /* RxCocoa */; }; + 05A7CBFE2E27DBDE0010F1CD /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7CBFD2E27DBDE0010F1CD /* RxSwift */; }; + 05A7CC002E27DBDE0010F1CD /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7CBFF2E27DBDE0010F1CD /* Then */; }; + 05A7CC032E27DC130010F1CD /* Pageboy in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7CC022E27DC130010F1CD /* Pageboy */; }; + 05A7CC0E2E28EC150010F1CD /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7CC0D2E28EC150010F1CD /* KakaoSDKAuth */; }; + 05A7CC102E28EC150010F1CD /* KakaoSDKUser in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7CC0F2E28EC150010F1CD /* KakaoSDKUser */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -66,10 +75,11 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + 055C24F12E28FD9800AD389C /* PresentationTesting.framework in Embed Frameworks */, 05A7C1BD2E2679FC0010F1CD /* Infrastructure.framework in Embed Frameworks */, 05A7C1B32E26799A0010F1CD /* LoginFeature.framework in Embed Frameworks */, 05A7C1B72E26799A0010F1CD /* LoginFeatureInterface.framework in Embed Frameworks */, - 05A7CBD92E27D3510010F1CD /* Presentation.framework in Embed Frameworks */, + 05A7CBF82E27DBB30010F1CD /* DesignSystem.framework in Embed Frameworks */, 05A7CBE22E27D44F0010F1CD /* Domain.framework in Embed Frameworks */, 05A7CBE02E27D44F0010F1CD /* Data.framework in Embed Frameworks */, 05A7CBE42E27D44F0010F1CD /* DomainInterface.framework in Embed Frameworks */, @@ -92,6 +102,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 055C24EF2E28FD9800AD389C /* PresentationTesting.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PresentationTesting.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05A7C15F2E26768A0010F1CD /* LoginFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LoginFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05A7C16E2E2676E60010F1CD /* LoginFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LoginFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05A7C1922E26771A0010F1CD /* LoginFeatureDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LoginFeatureDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -103,6 +114,7 @@ 05A7CBDC2E27D44F0010F1CD /* Data.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Data.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05A7CBDD2E27D44F0010F1CD /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05A7CBDE2E27D44F0010F1CD /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05A7CC072E27DC8E0010F1CD /* SearchFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SearchFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ @@ -163,13 +175,21 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 05A7CC032E27DC130010F1CD /* Pageboy in Frameworks */, 05A7C1BC2E2679FC0010F1CD /* Infrastructure.framework in Frameworks */, 05A7C1B22E26799A0010F1CD /* LoginFeature.framework in Frameworks */, + 05A7CBFA2E27DBDE0010F1CD /* ReactorKit in Frameworks */, + 05A7CC002E27DBDE0010F1CD /* Then in Frameworks */, 05A7C1B62E26799A0010F1CD /* LoginFeatureInterface.framework in Frameworks */, - 05A7CBD82E27D3510010F1CD /* Presentation.framework in Frameworks */, + 05A7CC102E28EC150010F1CD /* KakaoSDKUser in Frameworks */, + 05A7CC0E2E28EC150010F1CD /* KakaoSDKAuth in Frameworks */, 05A7CBE12E27D44F0010F1CD /* Domain.framework in Frameworks */, + 05A7CBFE2E27DBDE0010F1CD /* RxSwift in Frameworks */, + 055C24F02E28FD9800AD389C /* PresentationTesting.framework in Frameworks */, + 05A7CBFC2E27DBDE0010F1CD /* RxCocoa in Frameworks */, 05A7CBDF2E27D44F0010F1CD /* Data.framework in Frameworks */, 05A7CBE82E27D5400010F1CD /* SnapKit in Frameworks */, + 05A7CBF72E27DBB30010F1CD /* DesignSystem.framework in Frameworks */, 05A7CBE32E27D44F0010F1CD /* DomainInterface.framework in Frameworks */, 05A7CBDA2E27D3510010F1CD /* PresentationInterface.framework in Frameworks */, ); @@ -202,6 +222,8 @@ 05A7C1AD2E2679290010F1CD /* Frameworks */ = { isa = PBXGroup; children = ( + 055C24EF2E28FD9800AD389C /* PresentationTesting.framework */, + 05A7CC072E27DC8E0010F1CD /* SearchFeatureInterface.framework */, 05A7CBDC2E27D44F0010F1CD /* Data.framework */, 05A7CBDD2E27D44F0010F1CD /* Domain.framework */, 05A7CBDE2E27D44F0010F1CD /* DomainInterface.framework */, @@ -308,6 +330,13 @@ name = LoginFeatureDemo; packageProductDependencies = ( 05A7CBE72E27D5400010F1CD /* SnapKit */, + 05A7CBF92E27DBDE0010F1CD /* ReactorKit */, + 05A7CBFB2E27DBDE0010F1CD /* RxCocoa */, + 05A7CBFD2E27DBDE0010F1CD /* RxSwift */, + 05A7CBFF2E27DBDE0010F1CD /* Then */, + 05A7CC022E27DC130010F1CD /* Pageboy */, + 05A7CC0D2E28EC150010F1CD /* KakaoSDKAuth */, + 05A7CC0F2E28EC150010F1CD /* KakaoSDKUser */, ); productName = LoginFeatureDemo; productReference = 05A7C1922E26771A0010F1CD /* LoginFeatureDemo.app */; @@ -348,6 +377,9 @@ 05A7C1D42E267B130010F1CD /* XCRemoteSwiftPackageReference "RxSwift" */, 05A7CBB42E277AD20010F1CD /* XCRemoteSwiftPackageReference "SnapKit" */, 05A7CBB72E277AE30010F1CD /* XCRemoteSwiftPackageReference "Then" */, + 05A7CC012E27DC130010F1CD /* XCRemoteSwiftPackageReference "Pageboy" */, + 05A7CC042E27DC5E0010F1CD /* XCRemoteSwiftPackageReference "SPM-NMapsMap" */, + 05A7CC0A2E28EC030010F1CD /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */, ); preferredProjectObjectVersion = 77; productRefGroup = 05A7C1602E26768A0010F1CD /* Products */; @@ -853,6 +885,30 @@ minimumVersion = 3.0.0; }; }; + 05A7CC012E27DC130010F1CD /* XCRemoteSwiftPackageReference "Pageboy" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/uias/Pageboy"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 4.2.0; + }; + }; + 05A7CC042E27DC5E0010F1CD /* XCRemoteSwiftPackageReference "SPM-NMapsMap" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/navermaps/SPM-NMapsMap"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 3.22.0; + }; + }; + 05A7CC0A2E28EC030010F1CD /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/kakao/kakao-ios-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.24.5; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -886,6 +942,41 @@ package = 05A7CBB42E277AD20010F1CD /* XCRemoteSwiftPackageReference "SnapKit" */; productName = SnapKit; }; + 05A7CBF92E27DBDE0010F1CD /* ReactorKit */ = { + isa = XCSwiftPackageProductDependency; + package = 05A7C1D12E267AF80010F1CD /* XCRemoteSwiftPackageReference "ReactorKit" */; + productName = ReactorKit; + }; + 05A7CBFB2E27DBDE0010F1CD /* RxCocoa */ = { + isa = XCSwiftPackageProductDependency; + package = 05A7C1D42E267B130010F1CD /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxCocoa; + }; + 05A7CBFD2E27DBDE0010F1CD /* RxSwift */ = { + isa = XCSwiftPackageProductDependency; + package = 05A7C1D42E267B130010F1CD /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxSwift; + }; + 05A7CBFF2E27DBDE0010F1CD /* Then */ = { + isa = XCSwiftPackageProductDependency; + package = 05A7CBB72E277AE30010F1CD /* XCRemoteSwiftPackageReference "Then" */; + productName = Then; + }; + 05A7CC022E27DC130010F1CD /* Pageboy */ = { + isa = XCSwiftPackageProductDependency; + package = 05A7CC012E27DC130010F1CD /* XCRemoteSwiftPackageReference "Pageboy" */; + productName = Pageboy; + }; + 05A7CC0D2E28EC150010F1CD /* KakaoSDKAuth */ = { + isa = XCSwiftPackageProductDependency; + package = 05A7CC0A2E28EC030010F1CD /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; + productName = KakaoSDKAuth; + }; + 05A7CC0F2E28EC150010F1CD /* KakaoSDKUser */ = { + isa = XCSwiftPackageProductDependency; + package = 05A7CC0A2E28EC030010F1CD /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; + productName = KakaoSDKUser; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 05A7C1562E26768A0010F1CD /* Project object */; diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/AppDelegate.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/AppDelegate.swift index 35a41b5b..5f1ca8b9 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/AppDelegate.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/AppDelegate.swift @@ -6,14 +6,18 @@ import DomainInterface import Infrastructure import LoginFeature import LoginFeatureInterface -import Presentation import PresentationInterface +import PresentationTesting + +import KakaoSDKCommon @main class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { +// KakaoSDK.initSDK(appKey: Secrets.kakaoAuthAppKey) + self.registerDependencies() self.registerFactory() @@ -31,6 +35,7 @@ extension AppDelegate { private func registerDependencies() { // MARK: Register Service DIContainer.register(Provider.self) { return ProviderImpl() } + DIContainer.register(KeyChainService.self) { return KeyChainService() } // MARK: Resolve service @Dependency var provider: Provider @@ -53,8 +58,8 @@ extension AppDelegate { private func registerFactory() { DIContainer.register(LoginFactory.self) { return LoginFactoryImpl() } - DIContainer.register(SignUpFactory.self) { return SignUpFactoryImpl() } - DIContainer.register(WaveTabbarFactory.self) { return WaveTabbarFactoryImpl() } - DIContainer.register(FAQFactory.self) { return FAQFactoryImpl() } + DIContainer.register(SignUpFactory.self) { return SignUpFactoryMock() } + DIContainer.register(WaveTabbarFactory.self) { return WaveTabbarFactoryMock() } + DIContainer.register(FAQFactory.self) { return FAQFactoryMock() } } } diff --git a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj index 75b55bd5..c92cebc1 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj @@ -11,6 +11,8 @@ 05125BA12DB6275C001342A2 /* PanModal in Frameworks */ = {isa = PBXBuildFile; productRef = 05125BA02DB6275C001342A2 /* PanModal */; }; 051631302DC3D1FD00A6C0D1 /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0516312F2DC3D1FD00A6C0D1 /* DesignSystem.framework */; }; 0522C1E12DB67C8300B141FF /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0522C1E02DB67C8300B141FF /* RxSwift */; }; + 055C24D72E28FC6200AD389C /* PresentationInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C352DCDF6EC0093825D /* PresentationInterface.framework */; }; + 055C24E32E28FCA000AD389C /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0516312F2DC3D1FD00A6C0D1 /* DesignSystem.framework */; }; 05734C3C2DCDF6FE0093825D /* PresentationInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C352DCDF6EC0093825D /* PresentationInterface.framework */; }; 05734C412DCDF7190093825D /* SearchFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C402DCDF7190093825D /* SearchFeatureInterface.framework */; }; 05734C492DCDF7960093825D /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C482DCDF7960093825D /* DesignSystem.framework */; }; @@ -30,6 +32,13 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 055C24D92E28FC6200AD389C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 058CC8FB2DB537960084221A /* Project object */; + proxyType = 1; + remoteGlobalIDString = 05734C342DCDF6EC0093825D; + remoteInfo = PresentationInterface; + }; 05734C3E2DCDF6FE0093825D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 058CC8FB2DB537960084221A /* Project object */; @@ -41,6 +50,7 @@ /* Begin PBXFileReference section */ 0516312F2DC3D1FD00A6C0D1 /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 055C24D02E28FC3800AD389C /* PresentationTesting.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PresentationTesting.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05734C352DCDF6EC0093825D /* PresentationInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PresentationInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05734C402DCDF7190093825D /* SearchFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SearchFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05734C482DCDF7960093825D /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -50,6 +60,11 @@ /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ + 055C24D12E28FC3800AD389C /* PresentationTesting */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = PresentationTesting; + sourceTree = ""; + }; 05734C362DCDF6EC0093825D /* PresentationInterface */ = { isa = PBXFileSystemSynchronizedRootGroup; path = PresentationInterface; @@ -63,6 +78,15 @@ /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ + 055C24CD2E28FC3800AD389C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 055C24D72E28FC6200AD389C /* PresentationInterface.framework in Frameworks */, + 055C24E32E28FCA000AD389C /* DesignSystem.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 05734C322DCDF6EC0093825D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -105,6 +129,7 @@ children = ( 058CC9062DB537960084221A /* Presentation */, 05734C362DCDF6EC0093825D /* PresentationInterface */, + 055C24D12E28FC3800AD389C /* PresentationTesting */, 05C1D6292DB53A8200508FFD /* Frameworks */, 058CC9052DB537960084221A /* Products */, ); @@ -115,6 +140,7 @@ children = ( 058CC9042DB537960084221A /* Presentation.framework */, 05734C352DCDF6EC0093825D /* PresentationInterface.framework */, + 055C24D02E28FC3800AD389C /* PresentationTesting.framework */, ); name = Products; sourceTree = ""; @@ -134,6 +160,13 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ + 055C24CB2E28FC3800AD389C /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 05734C302DCDF6EC0093825D /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -151,6 +184,30 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + 055C24CF2E28FC3800AD389C /* PresentationTesting */ = { + isa = PBXNativeTarget; + buildConfigurationList = 055C24D62E28FC3800AD389C /* Build configuration list for PBXNativeTarget "PresentationTesting" */; + buildPhases = ( + 055C24CB2E28FC3800AD389C /* Headers */, + 055C24CC2E28FC3800AD389C /* Sources */, + 055C24CD2E28FC3800AD389C /* Frameworks */, + 055C24CE2E28FC3800AD389C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 055C24DA2E28FC6200AD389C /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + 055C24D12E28FC3800AD389C /* PresentationTesting */, + ); + name = PresentationTesting; + packageProductDependencies = ( + ); + productName = PresentationTesting; + productReference = 055C24D02E28FC3800AD389C /* PresentationTesting.framework */; + productType = "com.apple.product-type.framework"; + }; 05734C342DCDF6EC0093825D /* PresentationInterface */ = { isa = PBXNativeTarget; buildConfigurationList = 05734C3B2DCDF6EC0093825D /* Build configuration list for PBXNativeTarget "PresentationInterface" */; @@ -219,9 +276,12 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1630; + LastSwiftUpdateCheck = 1640; LastUpgradeCheck = 1630; TargetAttributes = { + 055C24CF2E28FC3800AD389C = { + CreatedOnToolsVersion = 16.4; + }; 05734C342DCDF6EC0093825D = { CreatedOnToolsVersion = 16.3; }; @@ -261,11 +321,19 @@ targets = ( 058CC9032DB537960084221A /* Presentation */, 05734C342DCDF6EC0093825D /* PresentationInterface */, + 055C24CF2E28FC3800AD389C /* PresentationTesting */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 055C24CE2E28FC3800AD389C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 05734C332DCDF6EC0093825D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -283,6 +351,13 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 055C24CC2E28FC3800AD389C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 05734C312DCDF6EC0093825D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -300,6 +375,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 055C24DA2E28FC6200AD389C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 05734C342DCDF6EC0093825D /* PresentationInterface */; + targetProxy = 055C24D92E28FC6200AD389C /* PBXContainerItemProxy */; + }; 05734C3F2DCDF6FE0093825D /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 05734C342DCDF6EC0093825D /* PresentationInterface */; @@ -308,6 +388,80 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + 055C24D42E28FC3800AD389C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.PresentationTesting; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_MODULE = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Debug; + }; + 055C24D52E28FC3800AD389C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.PresentationTesting; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_MODULE = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Release; + }; 05734C392DCDF6EC0093825D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -584,6 +738,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 055C24D62E28FC3800AD389C /* Build configuration list for PBXNativeTarget "PresentationTesting" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 055C24D42E28FC3800AD389C /* Debug */, + 055C24D52E28FC3800AD389C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 05734C3B2DCDF6EC0093825D /* Build configuration list for PBXNativeTarget "PresentationInterface" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Poppool/PresentationLayer/Presentation/PresentationTesting/Factory/FAQFactoryMock.swift b/Poppool/PresentationLayer/Presentation/PresentationTesting/Factory/FAQFactoryMock.swift new file mode 100644 index 00000000..a2fcd7e9 --- /dev/null +++ b/Poppool/PresentationLayer/Presentation/PresentationTesting/Factory/FAQFactoryMock.swift @@ -0,0 +1,11 @@ +import DesignSystem +import PresentationInterface + +public final class FAQFactoryMock: FAQFactory { + + public init() { } + + public func make() -> BaseViewController { + return BaseViewController() + } +} diff --git a/Poppool/PresentationLayer/Presentation/PresentationTesting/Factory/SignUpFactoryMock.swift b/Poppool/PresentationLayer/Presentation/PresentationTesting/Factory/SignUpFactoryMock.swift new file mode 100644 index 00000000..d39f424e --- /dev/null +++ b/Poppool/PresentationLayer/Presentation/PresentationTesting/Factory/SignUpFactoryMock.swift @@ -0,0 +1,11 @@ +import DesignSystem +import PresentationInterface + +public final class SignUpFactoryMock: SignUpFactory { + + public init() { } + + public func make(isFirstResponder: Bool = false, authrizationCode: String? = nil) -> BaseTabmanController { + return BaseTabmanController() + } +} diff --git a/Poppool/PresentationLayer/Presentation/PresentationTesting/Factory/WaveTabbarFactoryMock.swift b/Poppool/PresentationLayer/Presentation/PresentationTesting/Factory/WaveTabbarFactoryMock.swift new file mode 100644 index 00000000..2cfda98a --- /dev/null +++ b/Poppool/PresentationLayer/Presentation/PresentationTesting/Factory/WaveTabbarFactoryMock.swift @@ -0,0 +1,12 @@ +import UIKit + +import DesignSystem +import PresentationInterface + +public final class WaveTabbarFactoryMock: WaveTabbarFactory { + public init() {} + + public func make() -> UITabBarController { + return UITabBarController() + } +} From 60fda63e844956f6aa51bee4ca9665e58699a642 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 17 Jul 2025 19:52:10 +0900 Subject: [PATCH 25/56] =?UTF-8?q?refactor/#159:=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20feature=EC=9A=A9=20=EC=82=AC=EC=9D=B4=EB=8B=9D=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 --- Poppool/fastlane/Matchfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Poppool/fastlane/Matchfile b/Poppool/fastlane/Matchfile index dff1856f..0811e00d 100644 --- a/Poppool/fastlane/Matchfile +++ b/Poppool/fastlane/Matchfile @@ -4,5 +4,5 @@ storage_mode("git") type("development") # The default type, can be: appstore, adhoc, enterprise or development -app_identifier(["com.poppoolIOS.poppool", "com.poppoolIOS.*"]) +app_identifier(["com.poppoolIOS.poppool", "com.poppoolIOS.*", "com.poppoolIOS.poppool.LoginFeatureDemo"]) username("thddudgns972@gmail.com") \ No newline at end of file From 952fdb6bab30b832dc78c1b8c52e7644a789e624 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 18 Jul 2025 14:31:52 +0900 Subject: [PATCH 26/56] =?UTF-8?q?style/#160:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20output=20=ED=95=A0=EB=8B=B9=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoginFeature/LoginFeature/Login/LoginReactor.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift index 741cc4d3..590d6c84 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift @@ -60,8 +60,8 @@ final class LoginReactor: Reactor { case .appleButtonTapped(let controller): return loginWithApple(controller: controller) case .guestButtonTapped(let controller): - _ = keyChainService.deleteToken(type: .accessToken) - _ = keyChainService.deleteToken(type: .refreshToken) + keyChainService.deleteToken(type: .accessToken) + keyChainService.deleteToken(type: .refreshToken) return Observable.just(.moveToHomeScene(controller: controller)) case .inquiryButtonTapped(let controller): return Observable.just(.moveToInquiryScene(controller: controller)) From 1445932c170ae28c13178c256c6ceff3a19491d7 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 19 Jul 2025 15:44:07 +0900 Subject: [PATCH 27/56] =?UTF-8?q?refactor/#160:=20Reactor=EC=97=90?= =?UTF-8?q?=EC=84=9C=20Controller=20=EA=B2=B0=ED=95=A9=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoginFeature/Login/LoginReactor.swift | 80 +++++++++---------- .../Login/LoginViewController.swift | 60 ++++++++++---- 2 files changed, 84 insertions(+), 56 deletions(-) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift index 590d6c84..1fb929e6 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift @@ -11,20 +11,24 @@ final class LoginReactor: Reactor { // MARK: - Reactor enum Action { - case kakaoButtonTapped(controller: BaseViewController) - case appleButtonTapped(controller: BaseViewController) - case guestButtonTapped(controller: BaseViewController) - case inquiryButtonTapped(controller: BaseViewController) + case kakaoButtonTapped + case appleButtonTapped + case guestButtonTapped + case inquiryButtonTapped } enum Mutation { - case moveToSignUpScene(controller: BaseViewController) - case moveToHomeScene(controller: BaseViewController) + case moveToSignUpScene + case moveToHomeScene + case moveToInquiryScene case loadView - case moveToInquiryScene(controller: BaseViewController) } - struct State { } + struct State { + @Pulse var presentSignUp: String? + @Pulse var presentHome: Void? + @Pulse var presentInquiry: Void? + } // MARK: - properties @@ -55,51 +59,46 @@ final class LoginReactor: Reactor { // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { - case .kakaoButtonTapped(let controller): - return loginWithKakao(controller: controller) - case .appleButtonTapped(let controller): - return loginWithApple(controller: controller) - case .guestButtonTapped(let controller): + case .kakaoButtonTapped: + return loginWithKakao() + case .appleButtonTapped: + return loginWithApple() + case .guestButtonTapped: keyChainService.deleteToken(type: .accessToken) keyChainService.deleteToken(type: .refreshToken) - return Observable.just(.moveToHomeScene(controller: controller)) - case .inquiryButtonTapped(let controller): - return Observable.just(.moveToInquiryScene(controller: controller)) + return Observable.just(.moveToHomeScene) + case .inquiryButtonTapped: + return Observable.just(.moveToInquiryScene) } } func reduce(state: State, mutation: Mutation) -> State { + var newState = state + switch mutation { - case .moveToSignUpScene(let controller): - @Dependency var factory: SignUpFactory - controller.navigationController?.pushViewController( - factory.make(isFirstResponder: true, authrizationCode: authrizationCode), - animated: true - ) + case .moveToSignUpScene: + newState.presentSignUp = authrizationCode + + case .moveToHomeScene: + newState.presentHome = () - case .moveToHomeScene(let controller): - @Dependency var factory: WaveTabbarFactory - controller.view.window?.rootViewController = factory.make() + case .moveToInquiryScene: + newState.presentInquiry = () case .loadView: break - - case .moveToInquiryScene(let controller): - @Dependency var factory: FAQFactory - controller.navigationController?.pushViewController(factory.make(), animated: true) } - return state + return newState } - func loginWithKakao(controller: BaseViewController) -> Observable { + func loginWithKakao() -> Observable { return kakaoLoginUseCase.fetchUserCredential() .withUnretained(self) .flatMap { owner, response in return owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "kakao") } .withUnretained(self) - .map { [weak controller] (owner, loginResponse) in - guard let controller = controller else { return .loadView } + .map { (owner, loginResponse) in owner.userDefaultService.save(key: "userID", value: loginResponse.userId) owner.userDefaultService.save(key: "socialType", value: loginResponse.socialType) let accessTokenResult = owner.keyChainService.saveToken(type: .accessToken, value: loginResponse.accessToken) @@ -108,9 +107,9 @@ final class LoginReactor: Reactor { case .success: owner.userDefaultService.save(key: "lastLogin", value: "kakao") if loginResponse.isRegisteredUser { - return .moveToHomeScene(controller: controller) + return .moveToHomeScene } else { - return .moveToSignUpScene(controller: controller) + return .moveToSignUpScene } case .failure: return .loadView @@ -118,7 +117,7 @@ final class LoginReactor: Reactor { } } - func loginWithApple(controller: BaseViewController) -> Observable { + func loginWithApple() -> Observable { return appleLoginUseCase.fetchUserCredential() .withUnretained(self) .flatMap { owner, response in @@ -126,8 +125,7 @@ final class LoginReactor: Reactor { return owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "apple") } .withUnretained(self) - .map({ [weak controller] (owner, loginResponse) in - guard let controller = controller else { return .loadView } + .map { (owner, loginResponse) in owner.userDefaultService.save(key: "userID", value: loginResponse.userId) owner.userDefaultService.save(key: "socialType", value: loginResponse.socialType) let accessTokenResult = owner.keyChainService.saveToken(type: .accessToken, value: loginResponse.accessToken) @@ -136,13 +134,13 @@ final class LoginReactor: Reactor { case .success: owner.userDefaultService.save(key: "lastLogin", value: "apple") if loginResponse.isRegisteredUser { - return .moveToHomeScene(controller: controller) + return .moveToHomeScene } else { - return .moveToSignUpScene(controller: controller) + return .moveToSignUpScene } case .failure: return .loadView } - }) + } } } diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift index 4e48413f..94a47cb9 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift @@ -1,6 +1,8 @@ import UIKit import DesignSystem +import Infrastructure +import PresentationInterface import SnapKit import ReactorKit import RxCocoa @@ -40,36 +42,64 @@ extension LoginViewController { extension LoginViewController { func bind(reactor: Reactor) { + bindInput(reactor: reactor) + bindOutput(reactor: reactor) + } + + private func bindInput(reactor: Reactor) { mainView.guestButton.rx.tap - .withUnretained(self) - .map { (owner, _) in - Reactor.Action.guestButtonTapped(controller: owner) - } + .map { Reactor.Action.guestButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) mainView.kakaoButton.rx.tap - .withUnretained(self) - .map { (owner, _) in - Reactor.Action.kakaoButtonTapped(controller: owner) - } + .map { Reactor.Action.kakaoButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) mainView.inquiryButton.rx.tap - .withUnretained(self) - .map { (owner, _) in - Reactor.Action.inquiryButtonTapped(controller: owner) - } + .map { Reactor.Action.inquiryButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) mainView.appleButton.rx.tap + .map { Reactor.Action.appleButtonTapped } + .bind(to: reactor.action) + .disposed(by: disposeBag) + } + + private func bindOutput(reactor: Reactor) { + reactor.pulse(\.$presentSignUp) .withUnretained(self) - .map { (owner, _) in - Reactor.Action.appleButtonTapped(controller: owner) + .subscribe { (owner, authrizationCode) in + @Dependency var factory: SignUpFactory + owner.navigationController?.pushViewController( + factory.make( + isFirstResponder: true, + authrizationCode: authrizationCode + ), + animated: true + ) + } + .disposed(by: disposeBag) + + reactor.pulse(\.$presentHome) + .withUnretained(self) + .subscribe { (owner, _) in + @Dependency var factory: WaveTabbarFactory + owner.view.window?.rootViewController = factory.make() + } + .disposed(by: disposeBag) + + reactor.pulse(\.$presentInquiry) + .withUnretained(self) + .subscribe { (owner, _) in + @Dependency var factory: FAQFactory + owner.navigationController?.pushViewController( + factory.make(), + animated: true + ) } - .bind(to: reactor.action) .disposed(by: disposeBag) } } From 29150dd21a5fe534a1529d79244dda8383ad2ee0 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 19 Jul 2025 15:53:47 +0900 Subject: [PATCH 28/56] =?UTF-8?q?refactor/#160:=20Mutation=20none=20?= =?UTF-8?q?=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoginFeature/Login/LoginReactor.swift | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift index 1fb929e6..49b21fb3 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift @@ -21,7 +21,6 @@ final class LoginReactor: Reactor { case moveToSignUpScene case moveToHomeScene case moveToInquiryScene - case loadView } struct State { @@ -84,9 +83,6 @@ final class LoginReactor: Reactor { case .moveToInquiryScene: newState.presentInquiry = () - - case .loadView: - break } return newState } @@ -98,21 +94,27 @@ final class LoginReactor: Reactor { return owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "kakao") } .withUnretained(self) - .map { (owner, loginResponse) in + .flatMap { (owner, loginResponse) -> Observable in owner.userDefaultService.save(key: "userID", value: loginResponse.userId) owner.userDefaultService.save(key: "socialType", value: loginResponse.socialType) - let accessTokenResult = owner.keyChainService.saveToken(type: .accessToken, value: loginResponse.accessToken) - let refreshTokenResult = owner.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) + owner.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) + + let accessTokenResult = owner.keyChainService.saveToken( + type: .accessToken, + value: loginResponse.accessToken + ) + switch accessTokenResult { case .success: owner.userDefaultService.save(key: "lastLogin", value: "kakao") - if loginResponse.isRegisteredUser { - return .moveToHomeScene - } else { - return .moveToSignUpScene + + switch loginResponse.isRegisteredUser { + case true: return Observable.just(.moveToHomeScene) + case false: return Observable.just(.moveToSignUpScene) } + case .failure: - return .loadView + return Observable.empty() } } } @@ -125,21 +127,26 @@ final class LoginReactor: Reactor { return owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "apple") } .withUnretained(self) - .map { (owner, loginResponse) in + .flatMap { (owner, loginResponse) -> Observable in owner.userDefaultService.save(key: "userID", value: loginResponse.userId) owner.userDefaultService.save(key: "socialType", value: loginResponse.socialType) - let accessTokenResult = owner.keyChainService.saveToken(type: .accessToken, value: loginResponse.accessToken) - let refreshTokenResult = owner.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) + owner.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) + + let accessTokenResult = owner.keyChainService.saveToken( + type: .accessToken, + value: loginResponse.accessToken + ) switch accessTokenResult { case .success: owner.userDefaultService.save(key: "lastLogin", value: "apple") - if loginResponse.isRegisteredUser { - return .moveToHomeScene - } else { - return .moveToSignUpScene + + switch loginResponse.isRegisteredUser { + case true: return Observable.just(.moveToHomeScene) + case false: return Observable.just(.moveToSignUpScene) } + case .failure: - return .loadView + return Observable.empty() } } } From 0531b590de693bc7803d60bd8857b94ce27b08de Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 19 Jul 2025 15:55:05 +0900 Subject: [PATCH 29/56] =?UTF-8?q?refactor/#160:=20UserDefault=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9D=84=20keyType=20=ED=98=95=ED=83=9C=EB=A1=9C=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=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 --- .../Infrastructure/Service/UserDefaultService.swift | 3 +++ .../LoginFeature/Login/LoginReactor.swift | 12 ++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift index 52211c19..07ac612b 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift @@ -71,6 +71,9 @@ public final class UserDefaultService { extension UserDefaultService { public enum Key: String { case searchKeyword = "searchList" + case userID = "userID" + case socialType = "socialType" + case lastLogin = "lastLogin" } /// Userdefault 데이터 저장 메서드 diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift index 49b21fb3..a5274c8a 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift @@ -95,8 +95,8 @@ final class LoginReactor: Reactor { } .withUnretained(self) .flatMap { (owner, loginResponse) -> Observable in - owner.userDefaultService.save(key: "userID", value: loginResponse.userId) - owner.userDefaultService.save(key: "socialType", value: loginResponse.socialType) + owner.userDefaultService.save(keyType: .userID, value: loginResponse.userId) + owner.userDefaultService.save(keyType: .socialType, value: loginResponse.socialType) owner.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) let accessTokenResult = owner.keyChainService.saveToken( @@ -106,7 +106,7 @@ final class LoginReactor: Reactor { switch accessTokenResult { case .success: - owner.userDefaultService.save(key: "lastLogin", value: "kakao") + owner.userDefaultService.save(keyType: .lastLogin, value: "kakao") switch loginResponse.isRegisteredUser { case true: return Observable.just(.moveToHomeScene) @@ -128,8 +128,8 @@ final class LoginReactor: Reactor { } .withUnretained(self) .flatMap { (owner, loginResponse) -> Observable in - owner.userDefaultService.save(key: "userID", value: loginResponse.userId) - owner.userDefaultService.save(key: "socialType", value: loginResponse.socialType) + owner.userDefaultService.save(keyType: .userID, value: loginResponse.userId) + owner.userDefaultService.save(keyType: .socialType, value: loginResponse.socialType) owner.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) let accessTokenResult = owner.keyChainService.saveToken( @@ -138,7 +138,7 @@ final class LoginReactor: Reactor { ) switch accessTokenResult { case .success: - owner.userDefaultService.save(key: "lastLogin", value: "apple") + owner.userDefaultService.save(keyType: .lastLogin, value: "apple") switch loginResponse.isRegisteredUser { case true: return Observable.just(.moveToHomeScene) From fd17ac34f9bec5c0ea2eb7bbb95c23b906196544 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 19 Jul 2025 15:56:37 +0900 Subject: [PATCH 30/56] =?UTF-8?q?style/#160:=20switch=20case=20=EC=82=AC?= =?UTF-8?q?=EC=9D=B4=20=EA=B3=B5=EB=B0=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoginFeature/LoginFeature/Login/LoginReactor.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift index a5274c8a..9ba8b9ff 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift @@ -60,12 +60,15 @@ final class LoginReactor: Reactor { switch action { case .kakaoButtonTapped: return loginWithKakao() + case .appleButtonTapped: return loginWithApple() + case .guestButtonTapped: keyChainService.deleteToken(type: .accessToken) keyChainService.deleteToken(type: .refreshToken) return Observable.just(.moveToHomeScene) + case .inquiryButtonTapped: return Observable.just(.moveToInquiryScene) } From 0020298b0508c4310a257740e7e64303f62781d6 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 19 Jul 2025 16:40:25 +0900 Subject: [PATCH 31/56] =?UTF-8?q?refactor/#160:=20flatMap=20=EB=82=B4?= =?UTF-8?q?=EB=B6=80=EC=97=90=EC=84=9C=20=EB=8F=99=EC=9E=91=EC=9D=B4=20?= =?UTF-8?q?=EB=A7=8E=EB=8D=98=EA=B1=B8=20=EC=98=A4=ED=8D=BC=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoginFeature/Login/LoginReactor.swift | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift index 9ba8b9ff..e6eeccc5 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift @@ -68,7 +68,7 @@ final class LoginReactor: Reactor { keyChainService.deleteToken(type: .accessToken) keyChainService.deleteToken(type: .refreshToken) return Observable.just(.moveToHomeScene) - + case .inquiryButtonTapped: return Observable.just(.moveToInquiryScene) } @@ -87,21 +87,26 @@ final class LoginReactor: Reactor { case .moveToInquiryScene: newState.presentInquiry = () } + return newState } func loginWithKakao() -> Observable { return kakaoLoginUseCase.fetchUserCredential() .withUnretained(self) - .flatMap { owner, response in - return owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "kakao") + .flatMap { (owner, authServiceResponse) in + return owner.authAPIUseCase.postTryLogin( + userCredential: authServiceResponse, + socialType: "kakao" + ) } .withUnretained(self) - .flatMap { (owner, loginResponse) -> Observable in + .do { (owner, loginResponse) in owner.userDefaultService.save(keyType: .userID, value: loginResponse.userId) owner.userDefaultService.save(keyType: .socialType, value: loginResponse.socialType) owner.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) - + } + .flatMap { (owner, loginResponse) -> Observable in let accessTokenResult = owner.keyChainService.saveToken( type: .accessToken, value: loginResponse.accessToken @@ -116,7 +121,8 @@ final class LoginReactor: Reactor { case false: return Observable.just(.moveToSignUpScene) } - case .failure: + case .failure(let error): + // TODO: 로거 개선 후 로그인 실패 에러 남기기 return Observable.empty() } } @@ -125,20 +131,27 @@ final class LoginReactor: Reactor { func loginWithApple() -> Observable { return appleLoginUseCase.fetchUserCredential() .withUnretained(self) - .flatMap { owner, response in - owner.authrizationCode = response.authorizationCode - return owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "apple") + .do { (owner, authServiceResponse) in + owner.authrizationCode = authServiceResponse.authorizationCode + } + .flatMap { (owner, authServiceResponse) in + return owner.authAPIUseCase.postTryLogin( + userCredential: authServiceResponse, + socialType: "apple" + ) } .withUnretained(self) - .flatMap { (owner, loginResponse) -> Observable in + .do { (owner, loginResponse) in owner.userDefaultService.save(keyType: .userID, value: loginResponse.userId) owner.userDefaultService.save(keyType: .socialType, value: loginResponse.socialType) owner.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) - + } + .flatMap { (owner, loginResponse) -> Observable in let accessTokenResult = owner.keyChainService.saveToken( type: .accessToken, value: loginResponse.accessToken ) + switch accessTokenResult { case .success: owner.userDefaultService.save(keyType: .lastLogin, value: "apple") @@ -148,7 +161,8 @@ final class LoginReactor: Reactor { case false: return Observable.just(.moveToSignUpScene) } - case .failure: + case .failure(let error): + // TODO: 로거 개선 후 로그인 실패 에러 남기기 return Observable.empty() } } From 81e0df602a571194f76e222022c19c6bc1a34390 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 19 Jul 2025 16:40:46 +0900 Subject: [PATCH 32/56] =?UTF-8?q?refactor/#160:=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=ED=94=84=EB=A0=88=EC=9E=84=EC=9B=8C?= =?UTF-8?q?=ED=81=AC=20=EC=B1=84=ED=83=9D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Infrastructure/Infrastructure/Service/KeyChainService.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift index 82e82cd7..1ce0a244 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift @@ -1,8 +1,6 @@ import Foundation import Security -import RxSwift - public final class KeyChainService { // KeyChain에서 발생할 수 있는 오류를 정의 From e4d9de70efe762ec49cc8045221525866f2fdb10 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 19 Jul 2025 16:48:13 +0900 Subject: [PATCH 33/56] =?UTF-8?q?fix/#160:=20=EC=B9=B4=EC=B9=B4=EC=98=A4?= =?UTF-8?q?=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=EC=8B=9C=20=EC=95=A0?= =?UTF-8?q?=ED=94=8C=20=EC=9D=B8=EA=B0=80=20=EC=BD=94=EB=93=9C=EA=B0=80=20?= =?UTF-8?q?=EB=93=A4=EC=96=B4=EA=B0=80=EB=8A=94=20=EC=83=81=ED=99=A9=20?= =?UTF-8?q?=EB=B0=A9=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 애플 로그인으로 회원가입 시도 → 뒤로가기 → 카카오 로그인 시도 → 회원가입시 애플의 인가코드가 들어가는 문제 발생 가능 --- .../LoginFeature/LoginFeature/Login/LoginReactor.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift index e6eeccc5..f80811b3 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift @@ -94,6 +94,7 @@ final class LoginReactor: Reactor { func loginWithKakao() -> Observable { return kakaoLoginUseCase.fetchUserCredential() .withUnretained(self) + .do { (owner, _) in owner.authrizationCode = nil } .flatMap { (owner, authServiceResponse) in return owner.authAPIUseCase.postTryLogin( userCredential: authServiceResponse, From 386570928056f270ea27d36ee99d0fe8738a63dc Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 19 Jul 2025 17:07:26 +0900 Subject: [PATCH 34/56] =?UTF-8?q?refactor/#160:=20=EC=95=A0=ED=94=8C=20?= =?UTF-8?q?=EC=9D=B8=EA=B0=80=EC=BD=94=EB=93=9C=20=EC=A0=84=EB=8B=AC=20?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EC=A0=84=ED=99=98=20State=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoginFeature/Login/LoginReactor.swift | 56 ++++++++++--------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift index f80811b3..ae2861ba 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift @@ -18,7 +18,7 @@ final class LoginReactor: Reactor { } enum Mutation { - case moveToSignUpScene + case moveToSignUpScene(authrizationCode: String?) case moveToHomeScene case moveToInquiryScene } @@ -27,6 +27,14 @@ final class LoginReactor: Reactor { @Pulse var presentSignUp: String? @Pulse var presentHome: Void? @Pulse var presentInquiry: Void? + + @Pulse var present: PresentTarget? + } + + enum PresentTarget { + case signUp(authrizationCode: String?) + case home + case inquiry } // MARK: - properties @@ -34,7 +42,6 @@ final class LoginReactor: Reactor { var initialState: State var disposeBag = DisposeBag() - private var authrizationCode: String? private let authAPIUseCase: AuthAPIUseCase private let kakaoLoginUseCase: KakaoLoginUseCase @@ -78,14 +85,14 @@ final class LoginReactor: Reactor { var newState = state switch mutation { - case .moveToSignUpScene: - newState.presentSignUp = authrizationCode + case .moveToSignUpScene(let authrizationCode): + newState.present = .signUp(authrizationCode: authrizationCode) case .moveToHomeScene: - newState.presentHome = () + newState.present = .home case .moveToInquiryScene: - newState.presentInquiry = () + newState.present = .inquiry } return newState @@ -94,7 +101,6 @@ final class LoginReactor: Reactor { func loginWithKakao() -> Observable { return kakaoLoginUseCase.fetchUserCredential() .withUnretained(self) - .do { (owner, _) in owner.authrizationCode = nil } .flatMap { (owner, authServiceResponse) in return owner.authAPIUseCase.postTryLogin( userCredential: authServiceResponse, @@ -119,7 +125,7 @@ final class LoginReactor: Reactor { switch loginResponse.isRegisteredUser { case true: return Observable.just(.moveToHomeScene) - case false: return Observable.just(.moveToSignUpScene) + case false: return Observable.just(.moveToSignUpScene(authrizationCode: nil)) } case .failure(let error): @@ -132,39 +138,39 @@ final class LoginReactor: Reactor { func loginWithApple() -> Observable { return appleLoginUseCase.fetchUserCredential() .withUnretained(self) - .do { (owner, authServiceResponse) in - owner.authrizationCode = authServiceResponse.authorizationCode - } - .flatMap { (owner, authServiceResponse) in + .flatMap { (owner, authServiceResponse) -> Observable<(String?, LoginResponse)> in return owner.authAPIUseCase.postTryLogin( userCredential: authServiceResponse, socialType: "apple" ) + .map { (authServiceResponse.authorizationCode, $0) } } .withUnretained(self) - .do { (owner, loginResponse) in - owner.userDefaultService.save(keyType: .userID, value: loginResponse.userId) - owner.userDefaultService.save(keyType: .socialType, value: loginResponse.socialType) - owner.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) + .do { (owner, tuple) in + let (authCode, loginResponse) = tuple + self.userDefaultService.save(keyType: .userID, value: loginResponse.userId) + self.userDefaultService.save(keyType: .socialType, value: loginResponse.socialType) + self.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) } - .flatMap { (owner, loginResponse) -> Observable in - let accessTokenResult = owner.keyChainService.saveToken( + .flatMap { (owner, tuple) -> Observable in + let (authCode, loginResponse) = tuple + + let accessResult = self.keyChainService.saveToken( type: .accessToken, value: loginResponse.accessToken ) - - switch accessTokenResult { + switch accessResult { case .success: - owner.userDefaultService.save(keyType: .lastLogin, value: "apple") + self.userDefaultService.save(keyType: .lastLogin, value: "apple") switch loginResponse.isRegisteredUser { - case true: return Observable.just(.moveToHomeScene) - case false: return Observable.just(.moveToSignUpScene) + case true: return .just(.moveToHomeScene) + case false: return .just(.moveToSignUpScene(authrizationCode: authCode)) } - case .failure(let error): + case .failure: // TODO: 로거 개선 후 로그인 실패 에러 남기기 - return Observable.empty() + return .empty() } } } From 0c57da3d69e94736f0a74c6d8e8591fc56836e89 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 19 Jul 2025 17:12:58 +0900 Subject: [PATCH 35/56] =?UTF-8?q?refactor/#160:=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EB=90=9C=20present=20state=EB=A5=BC=20=EC=9D=B4=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=97=AC=20=ED=99=94=EB=A9=B4=20=EC=9D=B4=EB=8F=99=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoginFeature/Login/LoginReactor.swift | 5 -- .../Login/LoginViewController.swift | 54 +++++++++---------- 2 files changed, 25 insertions(+), 34 deletions(-) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift index ae2861ba..c5fb4a93 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift @@ -24,10 +24,6 @@ final class LoginReactor: Reactor { } struct State { - @Pulse var presentSignUp: String? - @Pulse var presentHome: Void? - @Pulse var presentInquiry: Void? - @Pulse var present: PresentTarget? } @@ -42,7 +38,6 @@ final class LoginReactor: Reactor { var initialState: State var disposeBag = DisposeBag() - private let authAPIUseCase: AuthAPIUseCase private let kakaoLoginUseCase: KakaoLoginUseCase private let appleLoginUseCase: AppleLoginUseCase diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift index 94a47cb9..4c1c0820 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift @@ -69,36 +69,32 @@ extension LoginViewController { } private func bindOutput(reactor: Reactor) { - reactor.pulse(\.$presentSignUp) + reactor.pulse(\.$present) + .skip(1) .withUnretained(self) - .subscribe { (owner, authrizationCode) in - @Dependency var factory: SignUpFactory - owner.navigationController?.pushViewController( - factory.make( - isFirstResponder: true, - authrizationCode: authrizationCode - ), - animated: true - ) - } - .disposed(by: disposeBag) - - reactor.pulse(\.$presentHome) - .withUnretained(self) - .subscribe { (owner, _) in - @Dependency var factory: WaveTabbarFactory - owner.view.window?.rootViewController = factory.make() - } - .disposed(by: disposeBag) - - reactor.pulse(\.$presentInquiry) - .withUnretained(self) - .subscribe { (owner, _) in - @Dependency var factory: FAQFactory - owner.navigationController?.pushViewController( - factory.make(), - animated: true - ) + .subscribe { (owner, target) in + switch target! { + case .signUp(let authrizationCode): + @Dependency var factory: SignUpFactory + owner.navigationController?.pushViewController( + factory.make( + isFirstResponder: true, + authrizationCode: authrizationCode + ), + animated: true + ) + + case .home: + @Dependency var factory: WaveTabbarFactory + owner.view.window?.rootViewController = factory.make() + + case .inquiry: + @Dependency var factory: FAQFactory + owner.navigationController?.pushViewController( + factory.make(), + animated: true + ) + } } .disposed(by: disposeBag) } From bd71214638fd2527ac65097766e73e0f6c593f88 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 19 Jul 2025 17:38:48 +0900 Subject: [PATCH 36/56] =?UTF-8?q?fix/#160:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=84=B1=EA=B3=B5=EC=8B=9C=EC=97=90=EB=A7=8C=20?= =?UTF-8?q?=EC=B5=9C=EA=B7=BC=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=ED=91=9C?= =?UTF-8?q?=EC=B6=9C=EB=90=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoginFeature/Login/LoginReactor.swift | 20 +++++++++++-------- .../Login/LoginViewController.swift | 2 +- .../Scene/SignUp/Main/SignUpMainReactor.swift | 5 +++++ 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift index c5fb4a93..318a090f 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift @@ -116,11 +116,13 @@ final class LoginReactor: Reactor { switch accessTokenResult { case .success: - owner.userDefaultService.save(keyType: .lastLogin, value: "kakao") - switch loginResponse.isRegisteredUser { - case true: return Observable.just(.moveToHomeScene) - case false: return Observable.just(.moveToSignUpScene(authrizationCode: nil)) + case true: + owner.userDefaultService.save(keyType: .lastLogin, value: "kakao") + return Observable.just(.moveToHomeScene) + + case false: + return Observable.just(.moveToSignUpScene(authrizationCode: nil)) } case .failure(let error): @@ -156,11 +158,13 @@ final class LoginReactor: Reactor { ) switch accessResult { case .success: - self.userDefaultService.save(keyType: .lastLogin, value: "apple") - switch loginResponse.isRegisteredUser { - case true: return .just(.moveToHomeScene) - case false: return .just(.moveToSignUpScene(authrizationCode: authCode)) + case true: + owner.userDefaultService.save(keyType: .lastLogin, value: "apple") + return .just(.moveToHomeScene) + + case false: + return .just(.moveToSignUpScene(authrizationCode: authCode)) } case .failure: diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift index 4c1c0820..c61ebfda 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift @@ -27,7 +27,7 @@ extension LoginViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - if let lastLogin = reactor?.userDefaultService.fetch(key: "lastLogin") { + if let lastLogin = reactor?.userDefaultService.fetch(keyType: .lastLogin) { switch lastLogin { case "kakao": mainView.kakaoButton.showToolTip(color: .w100, direction: .pointDown) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift index 3c5bafe2..f31dade0 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift @@ -135,6 +135,11 @@ final class SignUpMainReactor: Reactor { ) .subscribe { [weak self, weak controller] in guard let self = self else { return } + self.userDefaultService.save( + keyType: .lastLogin, + value: authrizationCode != nil ? "apple" : "kakao" + ) + let completeController = SignUpCompleteController() completeController.reactor = SignUpCompleteReactor( nickName: nickName, From b281cfd5a5e27a892717850f078d32e0393a70f8 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 19 Jul 2025 18:01:15 +0900 Subject: [PATCH 37/56] =?UTF-8?q?style/#160:=20import=20sort=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoginFeature/LoginFeature/Login/LoginViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift index c61ebfda..9c958708 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift @@ -3,10 +3,10 @@ import UIKit import DesignSystem import Infrastructure import PresentationInterface -import SnapKit import ReactorKit import RxCocoa import RxSwift +import SnapKit final class LoginViewController: BaseViewController, View { From f2ef29ed2bc7cffe01c4f286626d1897a5a6bf54 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 19 Jul 2025 18:49:50 +0900 Subject: [PATCH 38/56] =?UTF-8?q?refactor/#160:=20PPLabel=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=EA=B0=92=20=EC=A7=80=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 임시로 지정해준거고 기존 코드들에 문제없게 해두었습니다. - 추후 디자인 시스템 개편 과정에서 참고하도록 하겠습니다. --- .../DesignSystem/DesignSystem/Components/PPLabel.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPLabel.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPLabel.swift index 32ce068f..d27c4daf 100644 --- a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPLabel.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPLabel.swift @@ -3,8 +3,8 @@ import UIKit public class PPLabel: UILabel { public init( - style: UIFont.FontStyle, - fontSize: CGFloat, + style: UIFont.FontStyle = .regular, + fontSize: CGFloat = 12, text: String = "", lineHeight: CGFloat = 1.2 ) { From dd76f6e4744041eb64aa8eb05bf5202bf4653aca Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 19 Jul 2025 18:53:25 +0900 Subject: [PATCH 39/56] =?UTF-8?q?refactor/#160:=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=ED=99=94=EB=A9=B4=20=EC=A4=91=EB=B3=B5=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20=EB=86=92=EC=9D=80=20?= =?UTF-8?q?=EC=9C=A0=EC=A7=80=EB=B9=84=EC=9A=A9=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 로그인 화면과 서브 로그인 화면은 일부 컴포넌트의 속성을 제외하고는 동일한 동작을 가져감 - 서브로그인과 로그인을 리액터의 상태에 기반하여 구분되도록 수정 - 추후 로그인 개선시 하나의 코드만 수정해도 동일하게 동작하도록 개선 --- Poppool/Poppool/Application/AppDelegate.swift | 1 - .../LoginFeature/Factory/LoginFactory.swift | 3 +- .../Factory/SubLoginFactory.swift | 20 --- .../LoginFeature/Login/LoginReactor.swift | 45 ++++-- .../LoginFeature/Login/LoginView.swift | 34 +++-- .../Login/LoginViewController.swift | 30 +++- .../Login/Sub/SubLoginController.swift | 88 ----------- .../Login/Sub/SubLoginReactor.swift | 142 ------------------ .../LoginFeature/Login/Sub/SubLoginView.swift | 130 ---------------- .../LoginFeatureDemo/App/AppDelegate.swift | 4 - .../Source/Factory/LoginFactory.swift | 2 +- .../Source/Factory/SubLoginFactory.swift | 5 - .../Scene/Detail/DetailReactor.swift | 12 +- .../Scene/MyPage/Main/MyPageReactor.swift | 4 +- .../Scene/Splash/SplashController.swift | 2 +- 15 files changed, 101 insertions(+), 421 deletions(-) delete mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/SubLoginFactory.swift delete mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginController.swift delete mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginReactor.swift delete mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginView.swift delete mode 100644 Poppool/PresentationLayer/LoginFeature/LoginFeatureInterface/Source/Factory/SubLoginFactory.swift diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index d3059eb5..ddee740b 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -103,7 +103,6 @@ extension AppDelegate { DIContainer.register(CategorySelectorFactory.self) { return CategorySelectorFactoryImpl() } DIContainer.register(FilterSelectorFactory.self) { return FilterSelectorFactoryImpl() } DIContainer.register(LoginFactory.self) { return LoginFactoryImpl() } - DIContainer.register(SubLoginFactory.self) { return SubLoginFactoryImpl() } DIContainer.register(SignUpFactory.self) { return SignUpFactoryImpl() } DIContainer.register(WaveTabbarFactory.self) { return WaveTabbarFactoryImpl() } DIContainer.register(FAQFactory.self) { return FAQFactoryImpl() } diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/LoginFactory.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/LoginFactory.swift index 15b7cc8f..b622e8f2 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/LoginFactory.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/LoginFactory.swift @@ -9,9 +9,10 @@ public final class LoginFactoryImpl: LoginFactory { public init() { } - public func make() -> BaseViewController { + public func make(isSubLogin: Bool) -> BaseViewController { let viewController = LoginViewController() viewController.reactor = LoginReactor( + isSubLogin: isSubLogin, authAPIUseCase: DIContainer.resolve(AuthAPIUseCase.self), kakaoLoginUseCase: DIContainer.resolve(KakaoLoginUseCase.self), appleLoginUseCase: DIContainer.resolve(AppleLoginUseCase.self) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/SubLoginFactory.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/SubLoginFactory.swift deleted file mode 100644 index fc0de40e..00000000 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/SubLoginFactory.swift +++ /dev/null @@ -1,20 +0,0 @@ -import DesignSystem -import DomainInterface -import LoginFeatureInterface -import Infrastructure - -public final class SubLoginFactoryImpl: SubLoginFactory { - - public init() { } - - public func make() -> BaseViewController { - let viewController = SubLoginController() - viewController.reactor = SubLoginReactor( - authAPIUseCase: DIContainer.resolve(AuthAPIUseCase.self), - kakaoLoginUseCase: DIContainer.resolve(KakaoLoginUseCase.self), - appleLoginUseCase: DIContainer.resolve(AppleLoginUseCase.self) - ) - - return viewController - } -} diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift index 318a090f..30e88ac4 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift @@ -14,22 +14,27 @@ final class LoginReactor: Reactor { case kakaoButtonTapped case appleButtonTapped case guestButtonTapped + case xmarkButtonTapped case inquiryButtonTapped } enum Mutation { - case moveToSignUpScene(authrizationCode: String?) + case moveToSignUpScene(isSubLogin: Bool, authrizationCode: String?) case moveToHomeScene + case moveToBeforeScene case moveToInquiryScene } struct State { + var isSubLogin: Bool + @Pulse var present: PresentTarget? } enum PresentTarget { - case signUp(authrizationCode: String?) + case signUp(isFirstResponder: Bool, authrizationCode: String?) case home + case dismiss case inquiry } @@ -47,14 +52,15 @@ final class LoginReactor: Reactor { // MARK: - init init( + isSubLogin: Bool, authAPIUseCase: AuthAPIUseCase, kakaoLoginUseCase: KakaoLoginUseCase, appleLoginUseCase: AppleLoginUseCase ) { + self.initialState = State(isSubLogin: isSubLogin) self.authAPIUseCase = authAPIUseCase self.kakaoLoginUseCase = kakaoLoginUseCase self.appleLoginUseCase = appleLoginUseCase - self.initialState = State() } // MARK: - Reactor Methods @@ -71,6 +77,9 @@ final class LoginReactor: Reactor { keyChainService.deleteToken(type: .refreshToken) return Observable.just(.moveToHomeScene) + case .xmarkButtonTapped: + return Observable.just(.moveToBeforeScene) + case .inquiryButtonTapped: return Observable.just(.moveToInquiryScene) } @@ -80,12 +89,18 @@ final class LoginReactor: Reactor { var newState = state switch mutation { - case .moveToSignUpScene(let authrizationCode): - newState.present = .signUp(authrizationCode: authrizationCode) + case .moveToSignUpScene(let isSubLogin, let authrizationCode): + newState.present = .signUp( + isFirstResponder: !isSubLogin, + authrizationCode: authrizationCode + ) case .moveToHomeScene: newState.present = .home + case .moveToBeforeScene: + newState.present = .dismiss + case .moveToInquiryScene: newState.present = .inquiry } @@ -119,10 +134,17 @@ final class LoginReactor: Reactor { switch loginResponse.isRegisteredUser { case true: owner.userDefaultService.save(keyType: .lastLogin, value: "kakao") - return Observable.just(.moveToHomeScene) + return Observable.just( + owner.currentState.isSubLogin ? .moveToBeforeScene : .moveToHomeScene + ) case false: - return Observable.just(.moveToSignUpScene(authrizationCode: nil)) + return Observable.just( + .moveToSignUpScene( + isSubLogin: owner.currentState.isSubLogin, + authrizationCode: nil + ) + ) } case .failure(let error): @@ -161,10 +183,15 @@ final class LoginReactor: Reactor { switch loginResponse.isRegisteredUser { case true: owner.userDefaultService.save(keyType: .lastLogin, value: "apple") - return .just(.moveToHomeScene) + return .just(owner.currentState.isSubLogin ? .moveToBeforeScene : .moveToHomeScene) case false: - return .just(.moveToSignUpScene(authrizationCode: authCode)) + return .just( + .moveToSignUpScene( + isSubLogin: owner.currentState.isSubLogin, + authrizationCode: authCode + ) + ) } case .failure: diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginView.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginView.swift index 7dc9bb4d..e9fcbd50 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginView.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginView.swift @@ -14,19 +14,17 @@ final class LoginView: UIView { $0.setTitleColor(.g1000, for: .normal) } + let xmarkButton = UIButton(type: .system).then { + $0.setImage(UIImage(named: "icon_xmark"), for: .normal) + $0.tintColor = .g1000 + } + private let logoImageView = UIImageView().then { $0.image = UIImage(named: "image_login_logo") $0.contentMode = .scaleAspectFit } - private let titleLabel = PPLabel( - style: .bold, - fontSize: 16, - text: "간편하게 SNS 로그인하고\n팝풀 서비스를 이용해보세요" - ).then { - $0.numberOfLines = 0 - $0.textAlignment = .center - } + let titleLabel = PPLabel() let kakaoButton = PPButton(style: .kakao, text: "카카오톡으로 로그인") @@ -63,7 +61,7 @@ final class LoginView: UIView { private extension LoginView { func addViews() { - [guestButton, logoImageView, titleLabel, kakaoButton, appleButton, inquiryButton].forEach { + [guestButton, xmarkButton, logoImageView, titleLabel, kakaoButton, appleButton, inquiryButton].forEach { self.addSubview($0) } @@ -82,6 +80,12 @@ private extension LoginView { make.trailing.equalToSuperview().inset(20) } + xmarkButton.snp.makeConstraints { make in + make.top.equalTo(safeAreaLayoutGuide).inset(11) + make.trailing.equalToSuperview().inset(20) + make.size.equalTo(32) + } + logoImageView.snp.makeConstraints { make in make.height.equalTo(90) make.width.equalTo(70) @@ -124,3 +128,15 @@ private extension LoginView { } } } + +extension LoginView { + func setTitle(_ title: String) { + self.titleLabel.setLineHeightText( + text: title, + font: .korFont(style: .bold, size: 16), + lineHeight: 1.3 + ) + self.titleLabel.numberOfLines = 0 + self.titleLabel.textAlignment = .center + } +} diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift index 9c958708..1b2237e4 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift @@ -52,6 +52,11 @@ extension LoginViewController { .bind(to: reactor.action) .disposed(by: disposeBag) + mainView.xmarkButton.rx.tap + .map { Reactor.Action.xmarkButtonTapped } + .bind(to: reactor.action) + .disposed(by: disposeBag) + mainView.kakaoButton.rx.tap .map { Reactor.Action.kakaoButtonTapped } .bind(to: reactor.action) @@ -69,16 +74,34 @@ extension LoginViewController { } private func bindOutput(reactor: Reactor) { + reactor.state.distinctUntilChanged(\.isSubLogin) + .compactMap { $0.isSubLogin } + .withUnretained(self) + .subscribe { (owner, isSubLogin) in + switch isSubLogin { + case true: + owner.mainView.guestButton.isHidden = true + owner.mainView.xmarkButton.isHidden = false + owner.mainView.setTitle("간편하게 SNS 로그인하고\n공감가는 코멘트에 반응해볼까요?\n다른 코멘트를 확인해볼까요?") + + case false: + owner.mainView.guestButton.isHidden = false + owner.mainView.xmarkButton.isHidden = true + owner.mainView.setTitle("간편하게 SNS 로그인하고\n팝풀 서비스를 이용해보세요") + } + } + .disposed(by: disposeBag) + reactor.pulse(\.$present) .skip(1) .withUnretained(self) .subscribe { (owner, target) in switch target! { - case .signUp(let authrizationCode): + case .signUp(let isFirstResponder, let authrizationCode): @Dependency var factory: SignUpFactory owner.navigationController?.pushViewController( factory.make( - isFirstResponder: true, + isFirstResponder: isFirstResponder, authrizationCode: authrizationCode ), animated: true @@ -88,6 +111,9 @@ extension LoginViewController { @Dependency var factory: WaveTabbarFactory owner.view.window?.rootViewController = factory.make() + case .dismiss: + owner.dismiss(animated: true) + case .inquiry: @Dependency var factory: FAQFactory owner.navigationController?.pushViewController( diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginController.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginController.swift deleted file mode 100644 index 1b989757..00000000 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginController.swift +++ /dev/null @@ -1,88 +0,0 @@ -import UIKit - -import DesignSystem - -import ReactorKit -import RxCocoa -import RxSwift -import SnapKit - -final class SubLoginController: BaseViewController, View { - - typealias Reactor = SubLoginReactor - - // MARK: - Properties - var disposeBag = DisposeBag() - - private var mainView = SubLoginView() -} - -// MARK: - Life Cycle -extension SubLoginController { - override func viewDidLoad() { - super.viewDidLoad() - setUp() - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - if let lastLogin = reactor?.userDefaultService.fetch(key: "lastLogin") { - switch lastLogin { - case "kakao": - mainView.kakaoButton.showToolTip(color: .w100, direction: .pointDown) - case "apple": - mainView.appleButton.showToolTip(color: .w100, direction: .pointUp) - default: - break - } - } - } -} - -// MARK: - SetUp -private extension SubLoginController { - func setUp() { - view.backgroundColor = .g50 - view.addSubview(mainView) - mainView.snp.makeConstraints { make in - make.edges.equalTo(view.safeAreaLayoutGuide) - } - } -} - -// MARK: - Methods -extension SubLoginController { - func bind(reactor: Reactor) { - mainView.xmarkButton.rx.tap - .withUnretained(self) - .map { (owner, _) in - Reactor.Action.xmarkButtonTapped(controller: owner) - } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.kakaoButton.rx.tap - .withUnretained(self) - .map { (owner, _) in - Reactor.Action.kakaoButtonTapped(controller: owner) - } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.inquiryButton.rx.tap - .withUnretained(self) - .map { (owner, _) in - Reactor.Action.inquiryButtonTapped(controller: owner) - } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.appleButton.rx.tap - .withUnretained(self) - .map { (owner, _) in - Reactor.Action.appleButtonTapped(controller: owner) - } - .bind(to: reactor.action) - .disposed(by: disposeBag) - } -} diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginReactor.swift deleted file mode 100644 index 316fb996..00000000 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginReactor.swift +++ /dev/null @@ -1,142 +0,0 @@ -import DesignSystem -import DomainInterface -import Infrastructure -import PresentationInterface - -import ReactorKit -import RxCocoa -import RxSwift - -final class SubLoginReactor: Reactor { - - // MARK: - Reactor - enum Action { - case kakaoButtonTapped(controller: BaseViewController) - case appleButtonTapped(controller: BaseViewController) - case xmarkButtonTapped(controller: BaseViewController) - case inquiryButtonTapped(controller: BaseViewController) - } - - enum Mutation { - case moveToSignUpScene(controller: BaseViewController) - case dismissScene(controller: BaseViewController) - case loadView - case moveToInquiryScene(controller: BaseViewController) - } - - struct State { - } - - // MARK: - properties - - var initialState: State - var disposeBag = DisposeBag() - - private var authrizationCode: String? - - private let authAPIUseCase: AuthAPIUseCase - private let kakaoLoginUseCase: KakaoLoginUseCase - private let appleLoginUseCase: AppleLoginUseCase - @Dependency private var keyChainService: KeyChainService - let userDefaultService = UserDefaultService() - - // MARK: - init - init( - authAPIUseCase: AuthAPIUseCase, - kakaoLoginUseCase: KakaoLoginUseCase, - appleLoginUseCase: AppleLoginUseCase - ) { - self.authAPIUseCase = authAPIUseCase - self.kakaoLoginUseCase = kakaoLoginUseCase - self.appleLoginUseCase = appleLoginUseCase - self.initialState = State() - } - - // MARK: - Reactor Methods - func mutate(action: Action) -> Observable { - switch action { - case .kakaoButtonTapped(let controller): - return loginWithKakao(controller: controller) - case .appleButtonTapped(let controller): - return loginWithApple(controller: controller) - case .xmarkButtonTapped(let controller): - return Observable.just(.dismissScene(controller: controller)) - case .inquiryButtonTapped(let controller): - return Observable.just(.moveToInquiryScene(controller: controller)) - } - } - - func reduce(state: State, mutation: Mutation) -> State { - switch mutation { - case .moveToSignUpScene(let controller): - @Dependency var factory: SignUpFactory - controller.navigationController?.pushViewController( - factory.make(isFirstResponder: false, authrizationCode: authrizationCode), - animated: true - ) - case .dismissScene(let controller): - controller.dismiss(animated: true) - case .loadView: - break - case .moveToInquiryScene(let controller): - @Dependency var factory: FAQFactory - controller.navigationController?.pushViewController(factory.make(), animated: true) - } - return state - } - - func loginWithKakao(controller: BaseViewController) -> Observable { - return kakaoLoginUseCase.fetchUserCredential() - .withUnretained(self) - .flatMap { owner, response in - owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "kakao") - } - .withUnretained(self) - .map { [weak controller] (owner, loginResponse) in - guard let controller = controller else { return .loadView } - owner.userDefaultService.save(key: "userID", value: loginResponse.userId) - owner.userDefaultService.save(key: "socialType", value: loginResponse.socialType) - let accessTokenResult = owner.keyChainService.saveToken(type: .accessToken, value: loginResponse.accessToken) - let refreshTokenResult = owner.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) - switch accessTokenResult { - case .success: - owner.userDefaultService.save(key: "lastLogin", value: "kakao") - if loginResponse.isRegisteredUser { - return .dismissScene(controller: controller) - } else { - return .moveToSignUpScene(controller: controller) - } - case .failure: - return .loadView - } - } - } - - func loginWithApple(controller: BaseViewController) -> Observable { - return appleLoginUseCase.fetchUserCredential() - .withUnretained(self) - .flatMap { owner, response in - owner.authrizationCode = response.authorizationCode - return owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "apple") - } - .withUnretained(self) - .map { [weak controller] (owner, loginResponse) in - guard let controller = controller else { return .loadView } - owner.userDefaultService.save(key: "userID", value: loginResponse.userId) - owner.userDefaultService.save(key: "socialType", value: loginResponse.socialType) - let accessTokenResult = owner.keyChainService.saveToken(type: .accessToken, value: loginResponse.accessToken) - let refreshTokenResult = owner.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) - switch accessTokenResult { - case .success: - owner.userDefaultService.save(key: "lastLogin", value: "apple") - if loginResponse.isRegisteredUser { - return .dismissScene(controller: controller) - } else { - return .moveToSignUpScene(controller: controller) - } - case .failure: - return .loadView - } - } - } -} diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginView.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginView.swift deleted file mode 100644 index cea96869..00000000 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/Sub/SubLoginView.swift +++ /dev/null @@ -1,130 +0,0 @@ -import UIKit - -import DesignSystem - -import SnapKit - -final class SubLoginView: UIView { - - // MARK: - Components - let xmarkButton: UIButton = { - let button = UIButton(type: .system) - button.setImage(UIImage(named: "icon_xmark"), for: .normal) - button.tintColor = .g1000 - return button - }() - - private let logoImageView: UIImageView = { - let view = UIImageView() - view.image = UIImage(named: "image_login_logo") - view.contentMode = .scaleAspectFit - return view - }() - - private let titleLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 16, text: "간편하게 SNS 로그인하고\n공감가는 코멘트에 반응해볼까요?\n다른 코멘트를 확인해볼까요?") - label.setLineHeightText(text: "간편하게 SNS 로그인하고\n공감가는 코멘트에 반응해볼까요?\n다른 코멘트를 확인해볼까요?", font: .korFont(style: .bold, size: 16), lineHeight: 1.3) - label.numberOfLines = 0 - label.textAlignment = .center - return label - }() - - let kakaoButton: PPButton = { - return PPButton(style: .kakao, text: "카카오톡으로 로그인") - }() - - private let kakaoImageView: UIImageView = { - let view = UIImageView() - view.image = UIImage(named: "icon_login_kakao") - return view - }() - - let appleButton: PPButton = { - return PPButton(style: .apple, text: "Apple로 로그인") - }() - - private let appleImageView: UIImageView = { - let view = UIImageView() - view.image = UIImage(named: "icon_login_apple") - return view - }() - - let inquiryButton: UIButton = { - let button = UIButton(type: .system) - button.setTitle("로그인이 어려우신가요?", for: .normal) - button.titleLabel?.font = .korFont(style: .regular, size: 12) - button.setTitleColor(.g1000, for: .normal) - return button - }() - - // MARK: - init - init() { - super.init(frame: .zero) - setUpConstraints() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp -private extension SubLoginView { - - func setUpConstraints() { - self.addSubview(xmarkButton) - xmarkButton.snp.makeConstraints { make in - make.top.equalToSuperview().inset(11) - make.trailing.equalToSuperview().inset(20) - make.size.equalTo(32) - } - - self.addSubview(logoImageView) - logoImageView.snp.makeConstraints { make in - make.height.equalTo(90) - make.width.equalTo(70) - make.top.equalTo(xmarkButton.snp.bottom).offset(75) - make.centerX.equalToSuperview() - } - - self.addSubview(titleLabel) - titleLabel.snp.makeConstraints { make in - make.centerX.equalToSuperview() - make.top.equalTo(logoImageView.snp.bottom).offset(28) - } - - self.addSubview(kakaoButton) - kakaoButton.snp.makeConstraints { make in - make.top.equalTo(titleLabel.snp.bottom).offset(156) - make.leading.trailing.equalToSuperview().inset(20) - make.height.equalTo(50) - } - - kakaoButton.addSubview(kakaoImageView) - kakaoImageView.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.leading.equalToSuperview().inset(20) - make.size.equalTo(22) - } - - self.addSubview(appleButton) - appleButton.snp.makeConstraints { make in - make.top.equalTo(kakaoButton.snp.bottom).offset(16) - make.leading.trailing.equalToSuperview().inset(20) - make.height.equalTo(50) - } - - appleButton.addSubview(appleImageView) - appleImageView.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.leading.equalToSuperview().inset(20) - make.size.equalTo(22) - } - - self.addSubview(inquiryButton) - inquiryButton.snp.makeConstraints { make in - make.bottom.equalToSuperview().inset(56) - make.centerX.equalToSuperview() - } - } -} diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/AppDelegate.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/AppDelegate.swift index 5f1ca8b9..c6c764c7 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/AppDelegate.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/AppDelegate.swift @@ -9,15 +9,11 @@ import LoginFeatureInterface import PresentationInterface import PresentationTesting -import KakaoSDKCommon - @main class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { -// KakaoSDK.initSDK(appKey: Secrets.kakaoAuthAppKey) - self.registerDependencies() self.registerFactory() diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeatureInterface/Source/Factory/LoginFactory.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeatureInterface/Source/Factory/LoginFactory.swift index 4ddfe55d..18c5b420 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeatureInterface/Source/Factory/LoginFactory.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeatureInterface/Source/Factory/LoginFactory.swift @@ -3,5 +3,5 @@ import UIKit import DesignSystem public protocol LoginFactory { - func make() -> BaseViewController + func make(isSubLogin: Bool) -> BaseViewController } diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeatureInterface/Source/Factory/SubLoginFactory.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeatureInterface/Source/Factory/SubLoginFactory.swift deleted file mode 100644 index d205790b..00000000 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeatureInterface/Source/Factory/SubLoginFactory.swift +++ /dev/null @@ -1,5 +0,0 @@ -import DesignSystem - -public protocol SubLoginFactory { - func make() -> BaseViewController -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift index cefb9e93..0e2aed5b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift @@ -168,8 +168,8 @@ final class DetailReactor: Reactor { ) controller.navigationController?.pushViewController(commentController, animated: true) } else { - @Dependency var factory: SubLoginFactory - let nextController = UINavigationController(rootViewController: factory.make()) + @Dependency var factory: LoginFactory + let nextController = UINavigationController(rootViewController: factory.make(isSubLogin: true)) nextController.modalPresentationStyle = .fullScreen controller.present(nextController, animated: true) } @@ -203,8 +203,8 @@ final class DetailReactor: Reactor { ) controller.navigationController?.pushViewController(nextController, animated: true) } else { - @Dependency var factory: SubLoginFactory - let nextController = UINavigationController(rootViewController: factory.make()) + @Dependency var factory: LoginFactory + let nextController = UINavigationController(rootViewController: factory.make(isSubLogin: true)) nextController.modalPresentationStyle = .fullScreen controller.present(nextController, animated: true) } @@ -237,8 +237,8 @@ final class DetailReactor: Reactor { case .moveToRecentScene(let controller): controller.navigationController?.popViewController(animated: true) case .moveToLoginScene(let controller): - @Dependency var factory: SubLoginFactory - let nextController = UINavigationController(rootViewController: factory.make()) + @Dependency var factory: LoginFactory + let nextController = UINavigationController(rootViewController: factory.make(isSubLogin: true)) nextController.modalPresentationStyle = .fullScreen controller.present(nextController, animated: true) case .moveToImageDetailScene(let controller, let cellRow, let imageRow): diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift index fc6a78b6..c0cccd96 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift @@ -268,8 +268,8 @@ final class MyPageReactor: Reactor { controller.navigationController?.pushViewController(nextController, animated: true) case .moveToLoginScene(let controller): - @Dependency var factory: SubLoginFactory - let navigationController = UINavigationController(rootViewController: factory.make()) + @Dependency var factory: LoginFactory + let navigationController = UINavigationController(rootViewController: factory.make(isSubLogin: true)) navigationController.modalPresentationStyle = .fullScreen controller.present(navigationController, animated: true) case .moveToMyCommentScene(let controller): diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift index 44258e5c..d2a35ff2 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift @@ -64,7 +64,7 @@ private extension SplashController { }, onError: { [weak self] _ in guard let self = self else { return } @Dependency var factory: LoginFactory - let loginNavigationController = UINavigationController(rootViewController: factory.make()) + let loginNavigationController = UINavigationController(rootViewController: factory.make(isSubLogin: false)) rootViewController = loginNavigationController }) .disposed(by: disposeBag) From 08da954d2f081d537aa106591d1b3bf6cffc31c1 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Jul 2025 16:13:45 +0900 Subject: [PATCH 40/56] =?UTF-8?q?refactor/#160:=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EC=BD=94=EB=A9=98=ED=8A=B8=EB=A5=BC=20=EC=99=B8?= =?UTF-8?q?=EB=B6=80=EC=97=90=EC=84=9C=20=EC=A3=BC=EC=9E=85=EB=B0=9B?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 요구사항 분석과정에서 코멘트의 변경이 main/sub 만으로 나뉘지 않을것으로 예측함 - 기존에 isSubLogin이라는 bool로 분기를 하던것도 LoginSceneType을 별도로 선언하여 타입 추가에 대한 대비 --- .../LoginFeature/Factory/LoginFactory.swift | 10 ++-- .../LoginFeature/Login/LoginReactor.swift | 21 ++++---- .../LoginFeature/Login/LoginView.swift | 17 +++++++ .../Login/LoginViewController.swift | 50 ++++++++++++------- .../Source/Factory/LoginFactory.swift | 10 +++- .../Scene/Detail/DetailReactor.swift | 12 +++-- .../Scene/MyPage/Main/MyPageReactor.swift | 4 +- .../Scene/Splash/SplashController.swift | 26 ++++++---- 8 files changed, 104 insertions(+), 46 deletions(-) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/LoginFactory.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/LoginFactory.swift index b622e8f2..01002a48 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/LoginFactory.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/LoginFactory.swift @@ -9,10 +9,14 @@ public final class LoginFactoryImpl: LoginFactory { public init() { } - public func make(isSubLogin: Bool) -> BaseViewController { - let viewController = LoginViewController() + public func make( + _ loginSceneType: LoginSceneType, + text: String + ) -> BaseViewController { + let viewController = LoginViewController(loginSceneType: loginSceneType, text: text) + viewController.reactor = LoginReactor( - isSubLogin: isSubLogin, + for: loginSceneType, authAPIUseCase: DIContainer.resolve(AuthAPIUseCase.self), kakaoLoginUseCase: DIContainer.resolve(KakaoLoginUseCase.self), appleLoginUseCase: DIContainer.resolve(AppleLoginUseCase.self) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift index 30e88ac4..80fd4dbc 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift @@ -1,6 +1,7 @@ import DesignSystem import DomainInterface import Infrastructure +import LoginFeatureInterface import PresentationInterface import ReactorKit @@ -19,15 +20,13 @@ final class LoginReactor: Reactor { } enum Mutation { - case moveToSignUpScene(isSubLogin: Bool, authrizationCode: String?) + case moveToSignUpScene(from: LoginSceneType, authrizationCode: String?) case moveToHomeScene case moveToBeforeScene case moveToInquiryScene } struct State { - var isSubLogin: Bool - @Pulse var present: PresentTarget? } @@ -43,6 +42,7 @@ final class LoginReactor: Reactor { var initialState: State var disposeBag = DisposeBag() + private let loginSceneType: LoginSceneType private let authAPIUseCase: AuthAPIUseCase private let kakaoLoginUseCase: KakaoLoginUseCase private let appleLoginUseCase: AppleLoginUseCase @@ -52,12 +52,13 @@ final class LoginReactor: Reactor { // MARK: - init init( - isSubLogin: Bool, + for loginSceneType: LoginSceneType, authAPIUseCase: AuthAPIUseCase, kakaoLoginUseCase: KakaoLoginUseCase, appleLoginUseCase: AppleLoginUseCase ) { - self.initialState = State(isSubLogin: isSubLogin) + self.initialState = State() + self.loginSceneType = loginSceneType self.authAPIUseCase = authAPIUseCase self.kakaoLoginUseCase = kakaoLoginUseCase self.appleLoginUseCase = appleLoginUseCase @@ -91,7 +92,7 @@ final class LoginReactor: Reactor { switch mutation { case .moveToSignUpScene(let isSubLogin, let authrizationCode): newState.present = .signUp( - isFirstResponder: !isSubLogin, + isFirstResponder: loginSceneType == .main, authrizationCode: authrizationCode ) @@ -135,13 +136,13 @@ final class LoginReactor: Reactor { case true: owner.userDefaultService.save(keyType: .lastLogin, value: "kakao") return Observable.just( - owner.currentState.isSubLogin ? .moveToBeforeScene : .moveToHomeScene + owner.loginSceneType == .main ? .moveToHomeScene : .moveToBeforeScene ) case false: return Observable.just( .moveToSignUpScene( - isSubLogin: owner.currentState.isSubLogin, + from: owner.loginSceneType, authrizationCode: nil ) ) @@ -183,12 +184,12 @@ final class LoginReactor: Reactor { switch loginResponse.isRegisteredUser { case true: owner.userDefaultService.save(keyType: .lastLogin, value: "apple") - return .just(owner.currentState.isSubLogin ? .moveToBeforeScene : .moveToHomeScene) + return .just(owner.loginSceneType == .main ? .moveToHomeScene : .moveToBeforeScene) case false: return .just( .moveToSignUpScene( - isSubLogin: owner.currentState.isSubLogin, + from: owner.loginSceneType, authrizationCode: authCode ) ) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginView.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginView.swift index e9fcbd50..e24e2273 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginView.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginView.swift @@ -1,6 +1,7 @@ import UIKit import DesignSystem +import LoginFeatureInterface import SnapKit import Then @@ -12,11 +13,13 @@ final class LoginView: UIView { $0.setTitle("둘러보기", for: .normal) $0.titleLabel?.font = .korFont(style: .regular, size: 14) $0.setTitleColor(.g1000, for: .normal) + $0.isHidden = true } let xmarkButton = UIButton(type: .system).then { $0.setImage(UIImage(named: "icon_xmark"), for: .normal) $0.tintColor = .g1000 + $0.isHidden = true } private let logoImageView = UIImageView().then { @@ -55,6 +58,10 @@ final class LoginView: UIView { required init?(coder: NSCoder) { fatalError("\(#file), \(#function) Error") } + + deinit { + print("DEINIT DEBUG: \(#file)") + } } // MARK: - SetUp @@ -139,4 +146,14 @@ extension LoginView { self.titleLabel.numberOfLines = 0 self.titleLabel.textAlignment = .center } + + func setCloseButton(for loginSceneType: LoginSceneType) { + switch loginSceneType { + case .main: + self.guestButton.isHidden = false + + case .sub: + self.xmarkButton.isHidden = false + } + } } diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift index 1b2237e4..8e35a626 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift @@ -2,12 +2,27 @@ import UIKit import DesignSystem import Infrastructure +import LoginFeatureInterface import PresentationInterface import ReactorKit import RxCocoa import RxSwift import SnapKit +/// 다른 처리가 뭐가 있을까 +/// 1. 코멘트 타이틀 +/// 1-1. 외부로부터 주입(상황에 맞춰 적절한 코멘트를 띄우기 위함) +/// 2. 우상단 버튼의 형태 +/// 2-1. 둘러보기/xmark +/// 2-2. 버튼의 타입만 전달받도록? +/// 2-3. 사이즈가 달라서 어려움이 있다고 봄 +/// 3. 우상단 버튼의 동작 -> 클로저로 넘긴다면 Reactor로 어떻게 처리? +/// 3-1. 일단 받아두고 reactor로부터 명령이 오면 실행 +/// 3-2. 이러면 viewController를 만들 때 클로저를 넣어줘야됨. -> factory, init 변경 +/// 4. 배경 색상 +/// 4-1. g50 -> subLogin +/// 4-2. w100 -> main login + final class LoginViewController: BaseViewController, View { typealias Reactor = LoginReactor @@ -17,6 +32,24 @@ final class LoginViewController: BaseViewController, View { private var mainView = LoginView() + public convenience init( + loginSceneType: LoginSceneType, + text: String + ) { + self.init() + + self.mainView.setCloseButton(for: loginSceneType) + self.mainView.setTitle(text) + } + + private override init() { + super.init() + } + + public required init?(coder: NSCoder) { + fatalError("\(#file), \(#function) Error") + } + override func loadView() { self.view = mainView } @@ -74,23 +107,6 @@ extension LoginViewController { } private func bindOutput(reactor: Reactor) { - reactor.state.distinctUntilChanged(\.isSubLogin) - .compactMap { $0.isSubLogin } - .withUnretained(self) - .subscribe { (owner, isSubLogin) in - switch isSubLogin { - case true: - owner.mainView.guestButton.isHidden = true - owner.mainView.xmarkButton.isHidden = false - owner.mainView.setTitle("간편하게 SNS 로그인하고\n공감가는 코멘트에 반응해볼까요?\n다른 코멘트를 확인해볼까요?") - - case false: - owner.mainView.guestButton.isHidden = false - owner.mainView.xmarkButton.isHidden = true - owner.mainView.setTitle("간편하게 SNS 로그인하고\n팝풀 서비스를 이용해보세요") - } - } - .disposed(by: disposeBag) reactor.pulse(\.$present) .skip(1) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeatureInterface/Source/Factory/LoginFactory.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeatureInterface/Source/Factory/LoginFactory.swift index 18c5b420..86367e1b 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeatureInterface/Source/Factory/LoginFactory.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeatureInterface/Source/Factory/LoginFactory.swift @@ -2,6 +2,14 @@ import UIKit import DesignSystem +public enum LoginSceneType { + case main + case sub +} + public protocol LoginFactory { - func make(isSubLogin: Bool) -> BaseViewController + func make( + _ type: LoginSceneType, + text: String + ) -> BaseViewController } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift index 0e2aed5b..45d30d19 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift @@ -169,7 +169,9 @@ final class DetailReactor: Reactor { controller.navigationController?.pushViewController(commentController, animated: true) } else { @Dependency var factory: LoginFactory - let nextController = UINavigationController(rootViewController: factory.make(isSubLogin: true)) + let nextController = UINavigationController( + rootViewController: factory.make(.sub, text: "간편하게 SNS 로그인하고\n다른 코멘트를 확인해볼까요?") + ) nextController.modalPresentationStyle = .fullScreen controller.present(nextController, animated: true) } @@ -204,7 +206,9 @@ final class DetailReactor: Reactor { controller.navigationController?.pushViewController(nextController, animated: true) } else { @Dependency var factory: LoginFactory - let nextController = UINavigationController(rootViewController: factory.make(isSubLogin: true)) + let nextController = UINavigationController( + rootViewController: factory.make(.sub, text: "간편하게 SNS 로그인하고\n다른 코멘트를 확인해볼까요?") + ) nextController.modalPresentationStyle = .fullScreen controller.present(nextController, animated: true) } @@ -238,7 +242,9 @@ final class DetailReactor: Reactor { controller.navigationController?.popViewController(animated: true) case .moveToLoginScene(let controller): @Dependency var factory: LoginFactory - let nextController = UINavigationController(rootViewController: factory.make(isSubLogin: true)) + let nextController = UINavigationController( + rootViewController: factory.make(.sub, text: "간편하게 SNS 로그인하고\n다른 코멘트를 확인해볼까요?") + ) nextController.modalPresentationStyle = .fullScreen controller.present(nextController, animated: true) case .moveToImageDetailScene(let controller, let cellRow, let imageRow): diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift index c0cccd96..40d0b5ba 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift @@ -269,7 +269,9 @@ final class MyPageReactor: Reactor { case .moveToLoginScene(let controller): @Dependency var factory: LoginFactory - let navigationController = UINavigationController(rootViewController: factory.make(isSubLogin: true)) + let navigationController = UINavigationController( + rootViewController: factory.make(.sub, text: "간편하게 SNS 로그인하고\n팝풀 서비스를 이용해보세요") + ) navigationController.modalPresentationStyle = .fullScreen controller.present(navigationController, animated: true) case .moveToMyCommentScene(let controller): diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift index d2a35ff2..8817aed5 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift @@ -54,17 +54,21 @@ private extension SplashController { func setRootview() { authAPIUseCase.postTokenReissue() .withUnretained(self) - .subscribe(onNext: { (owner, response) in - let newAccessToken = response.accessToken ?? "" - let newRefreshToken = response.refreshToken ?? "" - _ = owner.keyChainService.saveToken(type: .accessToken, value: newAccessToken) - _ = owner.keyChainService.saveToken(type: .refreshToken, value: newRefreshToken) - let navigationController = WaveTabBarController() - owner.rootViewController = navigationController - }, onError: { [weak self] _ in - guard let self = self else { return } - @Dependency var factory: LoginFactory - let loginNavigationController = UINavigationController(rootViewController: factory.make(isSubLogin: false)) + .subscribe( + onNext: { (owner, response) in + let newAccessToken = response.accessToken ?? "" + let newRefreshToken = response.refreshToken ?? "" + _ = owner.keyChainService.saveToken(type: .accessToken, value: newAccessToken) + _ = owner.keyChainService.saveToken(type: .refreshToken, value: newRefreshToken) + let navigationController = WaveTabBarController() + owner.rootViewController = navigationController + }, + onError: { [weak self] _ in + guard let self = self else { return } + @Dependency var factory: LoginFactory + let loginNavigationController = UINavigationController( + rootViewController: factory.make(.main,text: "간편하게 SNS 로그인하고\n팝풀 서비스를 이용해보세요") + ) rootViewController = loginNavigationController }) .disposed(by: disposeBag) From fed18252576c7dd190fb1d4a5727491c14c7b07b Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Jul 2025 17:00:46 +0900 Subject: [PATCH 41/56] =?UTF-8?q?refactor/#160:=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EC=82=AC=EC=9D=B4=EB=8B=9D=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EB=8D=B4=ED=8B=B0=ED=8C=8C=EC=9D=B4=EC=96=B4=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/fastlane/Matchfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Poppool/fastlane/Matchfile b/Poppool/fastlane/Matchfile index 0811e00d..dff1856f 100644 --- a/Poppool/fastlane/Matchfile +++ b/Poppool/fastlane/Matchfile @@ -4,5 +4,5 @@ storage_mode("git") type("development") # The default type, can be: appstore, adhoc, enterprise or development -app_identifier(["com.poppoolIOS.poppool", "com.poppoolIOS.*", "com.poppoolIOS.poppool.LoginFeatureDemo"]) +app_identifier(["com.poppoolIOS.poppool", "com.poppoolIOS.*"]) username("thddudgns972@gmail.com") \ No newline at end of file From b05ef85950312c12ecc2a7d77ef8731d57c95108 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 21 Jul 2025 08:06:38 +0000 Subject: [PATCH 42/56] style/#160: Apply SwiftLint autocorrect --- Poppool/Poppool/Application/AppDelegate.swift | 4 ++-- .../LoginFeature/LoginFeature/Factory/LoginFactory.swift | 2 +- .../LoginFeature/LoginFeature/Login/LoginReactor.swift | 2 +- .../LoginFeature/LoginFeatureDemo/App/SceneDelegate.swift | 4 +--- .../Presentation/Scene/MyPage/Main/MyPageReactor.swift | 2 +- .../Scene/SignUp/FactoryImpl/SignUpFactoryImpl.swift | 2 +- .../Presentation/Scene/Splash/SplashController.swift | 2 +- 7 files changed, 8 insertions(+), 10 deletions(-) diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index ddee740b..2fbf16ec 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -5,12 +5,12 @@ import Data import Domain import DomainInterface import Infrastructure +import LoginFeature +import LoginFeatureInterface import Presentation import PresentationInterface import SearchFeature import SearchFeatureInterface -import LoginFeature -import LoginFeatureInterface import KakaoSDKCommon import NMapsMap diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/LoginFactory.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/LoginFactory.swift index 01002a48..1f87be78 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/LoginFactory.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Factory/LoginFactory.swift @@ -2,8 +2,8 @@ import UIKit import DesignSystem import DomainInterface -import LoginFeatureInterface import Infrastructure +import LoginFeatureInterface public final class LoginFactoryImpl: LoginFactory { diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift index 80fd4dbc..22de97d6 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift @@ -166,7 +166,7 @@ final class LoginReactor: Reactor { .map { (authServiceResponse.authorizationCode, $0) } } .withUnretained(self) - .do { (owner, tuple) in + .do { (_, tuple) in let (authCode, loginResponse) = tuple self.userDefaultService.save(keyType: .userID, value: loginResponse.userId) self.userDefaultService.save(keyType: .socialType, value: loginResponse.socialType) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/SceneDelegate.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/SceneDelegate.swift index 94b688cc..e9512bde 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/SceneDelegate.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/SceneDelegate.swift @@ -1,13 +1,12 @@ import UIKit -import LoginFeatureInterface import Infrastructure +import LoginFeatureInterface class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? - func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } @@ -25,4 +24,3 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { window?.makeKeyAndVisible() } } - diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift index 40d0b5ba..379f65a9 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift @@ -2,8 +2,8 @@ import UIKit import DesignSystem import DomainInterface -import LoginFeatureInterface import Infrastructure +import LoginFeatureInterface import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/FactoryImpl/SignUpFactoryImpl.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/FactoryImpl/SignUpFactoryImpl.swift index bcac3cbd..01228941 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/FactoryImpl/SignUpFactoryImpl.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/FactoryImpl/SignUpFactoryImpl.swift @@ -2,8 +2,8 @@ import UIKit import DesignSystem import DomainInterface -import PresentationInterface import Infrastructure +import PresentationInterface public final class SignUpFactoryImpl: SignUpFactory { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift index 8817aed5..42077e66 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift @@ -67,7 +67,7 @@ private extension SplashController { guard let self = self else { return } @Dependency var factory: LoginFactory let loginNavigationController = UINavigationController( - rootViewController: factory.make(.main,text: "간편하게 SNS 로그인하고\n팝풀 서비스를 이용해보세요") + rootViewController: factory.make(.main, text: "간편하게 SNS 로그인하고\n팝풀 서비스를 이용해보세요") ) rootViewController = loginNavigationController }) From 66b086de91d78fb5612a8f1926663404027c59cf Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Jul 2025 17:25:12 +0900 Subject: [PATCH 43/56] =?UTF-8?q?fix/#160:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=20=EB=8D=B0=EB=AA=A8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoginFeature/LoginFeatureDemo/App/SceneDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/SceneDelegate.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/SceneDelegate.swift index e9512bde..01261a1f 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/SceneDelegate.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/SceneDelegate.swift @@ -16,7 +16,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { @Dependency var factory: LoginFactory navigationController.pushViewController( - factory.make(), + factory.make(.main, text: "간편하게 SNS 로그인하고\n팝풀 서비스를 이용해보세요"), animated: false ) From 46c2c68a83c307a54abd6935e190d006640143e3 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Jul 2025 17:25:31 +0900 Subject: [PATCH 44/56] =?UTF-8?q?fix/#160:=20UserDefaultService=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=A3=BC=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoginFeature/LoginFeature/Login/LoginReactor.swift | 2 +- .../LoginFeature/LoginFeatureDemo/App/AppDelegate.swift | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift index 22de97d6..582222a3 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift @@ -48,7 +48,7 @@ final class LoginReactor: Reactor { private let appleLoginUseCase: AppleLoginUseCase @Dependency private var keyChainService: KeyChainService - let userDefaultService = UserDefaultService() + @Dependency private var userDefaultService: UserDefaultService // MARK: - init init( diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/AppDelegate.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/AppDelegate.swift index c6c764c7..a940ebaa 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/AppDelegate.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeatureDemo/App/AppDelegate.swift @@ -31,6 +31,7 @@ extension AppDelegate { private func registerDependencies() { // MARK: Register Service DIContainer.register(Provider.self) { return ProviderImpl() } + DIContainer.register(UserDefaultService.self) { return UserDefaultService() } DIContainer.register(KeyChainService.self) { return KeyChainService() } // MARK: Resolve service From a916d96f56603b1f9e5fefe4bbc272635f0575ea Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Jul 2025 17:59:11 +0900 Subject: [PATCH 45/56] =?UTF-8?q?fix/#160:=20userDefault=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=A0=9C=EA=B1=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 로그아웃이나 회원탈퇴시 정보 제거가 이루어지지 않고 있었음 - 해당 로직을 추가함 --- .../Infrastructure/Service/UserDefaultService.swift | 2 +- .../Scene/MyPage/Main/MyPageReactor.swift | 13 +++++++++++-- .../SelectedReason/WithdrawlReasonReactor.swift | 4 ++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift index 07ac612b..4a3f8fad 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift @@ -69,7 +69,7 @@ public final class UserDefaultService { // MARK: - Key base extension UserDefaultService { - public enum Key: String { + public enum Key: String, CaseIterable { case searchKeyword = "searchList" case userID = "userID" case socialType = "socialType" diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift index 379f65a9..4ad96b2b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift @@ -46,6 +46,8 @@ final class MyPageReactor: Reactor { private let userAPIUseCase: UserAPIUseCase + @Dependency private var userDefaultService: UserDefaultService + lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in guard let self = self else { @@ -162,7 +164,13 @@ final class MyPageReactor: Reactor { case .logoutButtonTapped: // 로그아웃 API return userAPIUseCase.postLogout() + .do(onCompleted: { [weak self] in + UserDefaultService.Key.allCases + .filter { $0 != .lastLogin } + .forEach { self?.userDefaultService.delete(keyType: $0) } + }) .andThen(Observable.just(.logout)) + .catch { error in Observable.empty() } case .adminMenuTapped(let controller): // 별도의 액션으로도 관리자 메뉴로 이동 가능 @@ -189,8 +197,9 @@ final class MyPageReactor: Reactor { case .logout: @Dependency var keyChainService: KeyChainService - _ = keyChainService.deleteToken(type: .accessToken) - _ = keyChainService.deleteToken(type: .refreshToken) + keyChainService.deleteToken(type: .accessToken) + keyChainService.deleteToken(type: .refreshToken) + ToastMaker.createToast(message: "로그아웃 되었어요") DispatchQueue.main.async { [weak self] in self?.action.onNext(.viewWillAppear) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift index a27dc32c..27e690b9 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift @@ -109,8 +109,8 @@ final class WithdrawlReasonReactor: Reactor { case .moveToCompleteScene(let controller): keyChainService.deleteToken(type: .accessToken) keyChainService.deleteToken(type: .refreshToken) - userDefaultService.delete(key: "lastLogin") - userDefaultService.delete(key: "searchList") + UserDefaultService.Key.allCases.forEach { userDefaultService.delete(keyType: $0) } + let nextController = WithdrawlCompleteController() nextController.mainView.checkButton.rx.tap .withUnretained(nextController) From 1561ad53d239d31e39761095d6b44924e1a7d348 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Jul 2025 18:16:43 +0900 Subject: [PATCH 46/56] =?UTF-8?q?refactor/#160:=20=ED=88=B4=ED=8C=81=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=EB=B0=A9=EC=8B=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - View에서 Reactor의 인스턴스(userDefautService)를 직접 접근하는것을 방지 - Rx의 ViewController LifeCycle을 이용하도록 수정 --- .../LoginFeature/Login/LoginReactor.swift | 25 +++++++++++- .../Login/LoginViewController.swift | 40 ++++++++++--------- 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift index 582222a3..764c724a 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift @@ -12,6 +12,7 @@ final class LoginReactor: Reactor { // MARK: - Reactor enum Action { + case viewWillAppear case kakaoButtonTapped case appleButtonTapped case guestButtonTapped @@ -24,9 +25,11 @@ final class LoginReactor: Reactor { case moveToHomeScene case moveToBeforeScene case moveToInquiryScene + case showTooltip(of: TooltipType?) } struct State { + var tooltipType: TooltipType? @Pulse var present: PresentTarget? } @@ -37,6 +40,10 @@ final class LoginReactor: Reactor { case inquiry } + enum TooltipType: String { + case kakao, apple + } + // MARK: - properties var initialState: State @@ -67,6 +74,9 @@ final class LoginReactor: Reactor { // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { + case .viewWillAppear: + return updateTooltip() + case .kakaoButtonTapped: return loginWithKakao() @@ -90,7 +100,10 @@ final class LoginReactor: Reactor { var newState = state switch mutation { - case .moveToSignUpScene(let isSubLogin, let authrizationCode): + case .showTooltip(let tooltipType): + newState.tooltipType = tooltipType + + case .moveToSignUpScene(let loginSceneType, let authrizationCode): newState.present = .signUp( isFirstResponder: loginSceneType == .main, authrizationCode: authrizationCode @@ -202,3 +215,13 @@ final class LoginReactor: Reactor { } } } + +extension LoginReactor { + private func updateTooltip() -> Observable { + if let lastLogin = userDefaultService.fetch(keyType: .lastLogin) { + return .just(.showTooltip(of: TooltipType(rawValue: lastLogin))) + } else { + return .empty() + } + } +} diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift index 8e35a626..8c31836b 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift @@ -55,24 +55,6 @@ final class LoginViewController: BaseViewController, View { } } -// MARK: - Life Cycle -extension LoginViewController { - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - if let lastLogin = reactor?.userDefaultService.fetch(keyType: .lastLogin) { - switch lastLogin { - case "kakao": - mainView.kakaoButton.showToolTip(color: .w100, direction: .pointDown) - case "apple": - mainView.appleButton.showToolTip(color: .w100, direction: .pointUp) - default: - break - } - } - } -} - extension LoginViewController { func bind(reactor: Reactor) { bindInput(reactor: reactor) @@ -80,6 +62,11 @@ extension LoginViewController { } private func bindInput(reactor: Reactor) { + rx.viewWillAppear + .map { Reactor.Action.viewWillAppear } + .bind(to: reactor.action) + .disposed(by: disposeBag) + mainView.guestButton.rx.tap .map { Reactor.Action.guestButtonTapped } .bind(to: reactor.action) @@ -107,7 +94,6 @@ extension LoginViewController { } private func bindOutput(reactor: Reactor) { - reactor.pulse(\.$present) .skip(1) .withUnretained(self) @@ -139,5 +125,21 @@ extension LoginViewController { } } .disposed(by: disposeBag) + + reactor.state.distinctUntilChanged(\.tooltipType) + .skip(1) + .map { $0.tooltipType } + .withUnretained(self) + .subscribe { (owner, type) in + switch type { + case .kakao: + owner.mainView.kakaoButton.showToolTip(color: .w100, direction: .pointDown) + case .apple: + owner.mainView.appleButton.showToolTip(color: .w100, direction: .pointUp) + case .none: + return + } + } + .disposed(by: disposeBag) } } From 58db22092711d71e5b499a8b2e85eedc4135ca7b Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Jul 2025 18:22:08 +0900 Subject: [PATCH 47/56] =?UTF-8?q?fix/#160:=20self=20=E2=86=92=20owner?= =?UTF-8?q?=EB=A5=BC=20=ED=86=B5=ED=95=B4=20=EC=95=BD=ED=95=9C=20=EC=B0=B8?= =?UTF-8?q?=EC=A1=B0=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoginFeature/Login/LoginReactor.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift index 764c724a..87a264d8 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift @@ -179,16 +179,16 @@ final class LoginReactor: Reactor { .map { (authServiceResponse.authorizationCode, $0) } } .withUnretained(self) - .do { (_, tuple) in - let (authCode, loginResponse) = tuple - self.userDefaultService.save(keyType: .userID, value: loginResponse.userId) - self.userDefaultService.save(keyType: .socialType, value: loginResponse.socialType) - self.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) + .do { (owner, tuple) in + let loginResponse = tuple.1 + owner.userDefaultService.save(keyType: .userID, value: loginResponse.userId) + owner.userDefaultService.save(keyType: .socialType, value: loginResponse.socialType) + owner.keyChainService.saveToken(type: .refreshToken, value: loginResponse.refreshToken) } .flatMap { (owner, tuple) -> Observable in let (authCode, loginResponse) = tuple - let accessResult = self.keyChainService.saveToken( + let accessResult = owner.keyChainService.saveToken( type: .accessToken, value: loginResponse.accessToken ) From dc13c38be06a4281de8f2fa8ede54ddf40f3d676 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Jul 2025 18:30:05 +0900 Subject: [PATCH 48/56] =?UTF-8?q?style/#160:=20factory=EB=A5=BC=20?= =?UTF-8?q?=EC=9D=B4=EC=9A=A9=ED=95=98=EC=97=AC=20=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EC=A0=84=ED=99=98=EC=9D=84=20=EC=84=A4=EA=B3=84=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=9D=BC=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Presentation/Scene/Splash/SplashController.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift index 42077e66..ca7a4e9f 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift @@ -4,6 +4,7 @@ import DesignSystem import DomainInterface import Infrastructure import LoginFeatureInterface +import PresentationInterface import ReactorKit import RxCocoa @@ -58,10 +59,10 @@ private extension SplashController { onNext: { (owner, response) in let newAccessToken = response.accessToken ?? "" let newRefreshToken = response.refreshToken ?? "" - _ = owner.keyChainService.saveToken(type: .accessToken, value: newAccessToken) - _ = owner.keyChainService.saveToken(type: .refreshToken, value: newRefreshToken) - let navigationController = WaveTabBarController() - owner.rootViewController = navigationController + owner.keyChainService.saveToken(type: .accessToken, value: newAccessToken) + owner.keyChainService.saveToken(type: .refreshToken, value: newRefreshToken) + @Dependency var factory: WaveTabbarFactory + owner.rootViewController = factory.make() }, onError: { [weak self] _ in guard let self = self else { return } From 24a8aa2a18bf7eaa11e4d3cc078b31854555adbd Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Jul 2025 18:31:43 +0900 Subject: [PATCH 49/56] =?UTF-8?q?style/#160:=20=EC=98=A4=ED=83=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - authrization → authorization --- .../LoginFeature/Login/LoginViewController.swift | 2 +- .../Scene/SignUp/FactoryImpl/SignUpFactoryImpl.swift | 4 ++-- .../Scene/SignUp/Main/SignUpMainReactor.swift | 10 +++++----- .../PresentationInterface/Factory/SignUpFactory.swift | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift index 8c31836b..d9023f9a 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift @@ -104,7 +104,7 @@ extension LoginViewController { owner.navigationController?.pushViewController( factory.make( isFirstResponder: isFirstResponder, - authrizationCode: authrizationCode + authorizationCode: authrizationCode ), animated: true ) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/FactoryImpl/SignUpFactoryImpl.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/FactoryImpl/SignUpFactoryImpl.swift index 01228941..c52ffa26 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/FactoryImpl/SignUpFactoryImpl.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/FactoryImpl/SignUpFactoryImpl.swift @@ -9,12 +9,12 @@ public final class SignUpFactoryImpl: SignUpFactory { public init() { } - public func make(isFirstResponder: Bool, authrizationCode: String?) -> DesignSystem.BaseTabmanController { + public func make(isFirstResponder: Bool, authorizationCode: String?) -> DesignSystem.BaseTabmanController { let viewController = SignUpMainController() viewController.reactor = SignUpMainReactor( isFirstResponderCase: isFirstResponder, - authrizationCode: authrizationCode, + authorizationCode: authorizationCode, signUpAPIUseCase: DIContainer.resolve(SignUpAPIUseCase.self) ) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift index f31dade0..f6dd0093 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift @@ -55,7 +55,7 @@ final class SignUpMainReactor: Reactor { var initialState: State var disposeBag = DisposeBag() - private var authrizationCode: String? + private var authorizationCode: String? private let signUpAPIUseCase: SignUpAPIUseCase private let userDefaultService = UserDefaultService() @@ -64,11 +64,11 @@ final class SignUpMainReactor: Reactor { // MARK: - init init( isFirstResponderCase: Bool, - authrizationCode: String?, + authorizationCode: String?, signUpAPIUseCase: SignUpAPIUseCase ) { self.initialState = State() - self.authrizationCode = authrizationCode + self.authorizationCode = authorizationCode self.isFirstResponderCase = isFirstResponderCase self.signUpAPIUseCase = signUpAPIUseCase } @@ -131,13 +131,13 @@ final class SignUpMainReactor: Reactor { socialEmail: "", socialType: socialType, interests: newState.categorys, - appleAuthorizationCode: authrizationCode + appleAuthorizationCode: authorizationCode ) .subscribe { [weak self, weak controller] in guard let self = self else { return } self.userDefaultService.save( keyType: .lastLogin, - value: authrizationCode != nil ? "apple" : "kakao" + value: authorizationCode != nil ? "apple" : "kakao" ) let completeController = SignUpCompleteController() diff --git a/Poppool/PresentationLayer/Presentation/PresentationInterface/Factory/SignUpFactory.swift b/Poppool/PresentationLayer/Presentation/PresentationInterface/Factory/SignUpFactory.swift index a2d2a667..58bec50d 100644 --- a/Poppool/PresentationLayer/Presentation/PresentationInterface/Factory/SignUpFactory.swift +++ b/Poppool/PresentationLayer/Presentation/PresentationInterface/Factory/SignUpFactory.swift @@ -1,5 +1,5 @@ import DesignSystem public protocol SignUpFactory { - func make(isFirstResponder: Bool, authrizationCode: String?) -> BaseTabmanController + func make(isFirstResponder: Bool, authorizationCode: String?) -> BaseTabmanController } From b953652c1dee098aa22ddf6b88985d83dde11345 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 21 Jul 2025 09:32:13 +0000 Subject: [PATCH 50/56] style/#160: Apply SwiftLint autocorrect --- .../Presentation/Scene/MyPage/Main/MyPageReactor.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift index 4ad96b2b..e731d238 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift @@ -170,7 +170,7 @@ final class MyPageReactor: Reactor { .forEach { self?.userDefaultService.delete(keyType: $0) } }) .andThen(Observable.just(.logout)) - .catch { error in Observable.empty() } + .catch { _ in Observable.empty() } case .adminMenuTapped(let controller): // 별도의 액션으로도 관리자 메뉴로 이동 가능 From a3ff19792c03839b2bafd0b0e35558884cf37832 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Jul 2025 18:55:14 +0900 Subject: [PATCH 51/56] =?UTF-8?q?fix/#160:=20deployment=20target=20?= =?UTF-8?q?=EB=B2=84=EC=A0=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoginFeature/LoginFeature.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/project.pbxproj index 959b38e5..8ff84d77 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/project.pbxproj @@ -513,7 +513,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.5; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -573,7 +573,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.5; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; From 5b2fd4e7113c1aac57a5ac4a91d7f2bbfa037a39 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Jul 2025 18:57:08 +0900 Subject: [PATCH 52/56] =?UTF-8?q?style/#160:=20=EC=98=A4=ED=83=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - authrization → authorization --- .../LoginFeature/Login/LoginReactor.swift | 12 ++++++------ .../LoginFeature/Login/LoginViewController.swift | 4 ++-- .../Factory/SignUpFactoryMock.swift | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift index 87a264d8..de8ff39a 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginReactor.swift @@ -21,7 +21,7 @@ final class LoginReactor: Reactor { } enum Mutation { - case moveToSignUpScene(from: LoginSceneType, authrizationCode: String?) + case moveToSignUpScene(from: LoginSceneType, authorizationCode: String?) case moveToHomeScene case moveToBeforeScene case moveToInquiryScene @@ -34,7 +34,7 @@ final class LoginReactor: Reactor { } enum PresentTarget { - case signUp(isFirstResponder: Bool, authrizationCode: String?) + case signUp(isFirstResponder: Bool, authorizationCode: String?) case home case dismiss case inquiry @@ -103,10 +103,10 @@ final class LoginReactor: Reactor { case .showTooltip(let tooltipType): newState.tooltipType = tooltipType - case .moveToSignUpScene(let loginSceneType, let authrizationCode): + case .moveToSignUpScene(let loginSceneType, let authorizationCode): newState.present = .signUp( isFirstResponder: loginSceneType == .main, - authrizationCode: authrizationCode + authorizationCode: authorizationCode ) case .moveToHomeScene: @@ -156,7 +156,7 @@ final class LoginReactor: Reactor { return Observable.just( .moveToSignUpScene( from: owner.loginSceneType, - authrizationCode: nil + authorizationCode: nil ) ) } @@ -203,7 +203,7 @@ final class LoginReactor: Reactor { return .just( .moveToSignUpScene( from: owner.loginSceneType, - authrizationCode: authCode + authorizationCode: authCode ) ) } diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift index d9023f9a..eb6d46e2 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature/Login/LoginViewController.swift @@ -99,12 +99,12 @@ extension LoginViewController { .withUnretained(self) .subscribe { (owner, target) in switch target! { - case .signUp(let isFirstResponder, let authrizationCode): + case .signUp(let isFirstResponder, let authorizationCode): @Dependency var factory: SignUpFactory owner.navigationController?.pushViewController( factory.make( isFirstResponder: isFirstResponder, - authorizationCode: authrizationCode + authorizationCode: authorizationCode ), animated: true ) diff --git a/Poppool/PresentationLayer/Presentation/PresentationTesting/Factory/SignUpFactoryMock.swift b/Poppool/PresentationLayer/Presentation/PresentationTesting/Factory/SignUpFactoryMock.swift index d39f424e..81ce3c56 100644 --- a/Poppool/PresentationLayer/Presentation/PresentationTesting/Factory/SignUpFactoryMock.swift +++ b/Poppool/PresentationLayer/Presentation/PresentationTesting/Factory/SignUpFactoryMock.swift @@ -5,7 +5,7 @@ public final class SignUpFactoryMock: SignUpFactory { public init() { } - public func make(isFirstResponder: Bool = false, authrizationCode: String? = nil) -> BaseTabmanController { + public func make(isFirstResponder: Bool = false, authorizationCode: String? = nil) -> BaseTabmanController { return BaseTabmanController() } } From ac526ae682269394bfa2f44b7ea8e599f63d4c61 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 25 Jul 2025 15:30:21 +0900 Subject: [PATCH 53/56] =?UTF-8?q?chore:=20package.resolve=20=EB=A7=A4?= =?UTF-8?q?=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xcshareddata/swiftpm/Package.resolved | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Poppool/Poppool.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Poppool/Poppool.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7bcd7a79..7dd10a0a 100644 --- a/Poppool/Poppool.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Poppool/Poppool.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "e3308f4d8d004a111784c1f182de9af75f8c85dd7807e55541802130c4c227fb", + "originHash" : "cfbe5317064ac0a76085850cc8ce600d1a395f57b89afed38711275f4a5aee39", "pins" : [ { "identity" : "alamofire", @@ -24,8 +24,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/kakao/kakao-ios-sdk.git", "state" : { - "revision" : "2c780b70d4197be3f72deee2fd82ea3e3b767007", - "version" : "2.24.1" + "revision" : "394ccfc67b206894f55dcaf2dd15e06bf451c4d3", + "version" : "2.24.5" } }, { @@ -123,8 +123,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/navermaps/SPM-NMapsMap", "state" : { - "revision" : "ad89e53fdfec3b8d8994280fb0414b5a7b1c3e8e", - "version" : "3.21.0" + "revision" : "13a6d280a57c4ebab8320e2d5bf3ce89adacf95e", + "version" : "3.22.0" } }, { From d6d80431a67a501944a6f0d67543fd5d45d8c6e1 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 25 Jul 2025 15:31:33 +0900 Subject: [PATCH 54/56] =?UTF-8?q?fix:=20http=20=ED=86=B5=EC=8B=A0=20?= =?UTF-8?q?=EC=9E=84=EC=8B=9C=20=ED=97=88=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/Poppool/Resource/Info.plist | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Poppool/Poppool/Resource/Info.plist b/Poppool/Poppool/Resource/Info.plist index 3ad9f07b..98e00bfb 100644 --- a/Poppool/Poppool/Resource/Info.plist +++ b/Poppool/Poppool/Resource/Info.plist @@ -2,6 +2,11 @@ + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + ITSAppUsesNonExemptEncryption CFBundleShortVersionString From f3b05b13d85e22326d514098c3ffe3ef6df92993 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 25 Jul 2025 15:49:34 +0900 Subject: [PATCH 55/56] =?UTF-8?q?fix:=20=ED=94=84=EB=A0=88=EC=9E=84?= =?UTF-8?q?=EC=9B=8C=ED=81=AC=20=EC=A4=91=EC=B2=A9=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoginFeature.xcodeproj/project.pbxproj | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/project.pbxproj index 8ff84d77..72bd2de2 100644 --- a/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/LoginFeature/LoginFeature.xcodeproj/project.pbxproj @@ -22,7 +22,6 @@ 05A7C1D82E267B130010F1CD /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7C1D72E267B130010F1CD /* RxSwift */; }; 05A7CBB92E277AE30010F1CD /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = 05A7CBB82E277AE30010F1CD /* Then */; }; 05A7CBD32E27D0700010F1CD /* PresentationInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBD22E27D0700010F1CD /* PresentationInterface.framework */; }; - 05A7CBD42E27D0700010F1CD /* PresentationInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBD22E27D0700010F1CD /* PresentationInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05A7CBDA2E27D3510010F1CD /* PresentationInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBD72E27D3510010F1CD /* PresentationInterface.framework */; }; 05A7CBDB2E27D3510010F1CD /* PresentationInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBD72E27D3510010F1CD /* PresentationInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05A7CBDF2E27D44F0010F1CD /* Data.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A7CBDC2E27D44F0010F1CD /* Data.framework */; }; @@ -88,17 +87,6 @@ name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; - 05A7CBD52E27D0700010F1CD /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 05A7CBD42E27D0700010F1CD /* PresentationInterface.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -264,7 +252,6 @@ 05A7C15B2E26768A0010F1CD /* Sources */, 05A7C15C2E26768A0010F1CD /* Frameworks */, 05A7C15D2E26768A0010F1CD /* Resources */, - 05A7CBD52E27D0700010F1CD /* Embed Frameworks */, ); buildRules = ( ); From 6de17e605d930762b49a27a4a0b185d3789e2a8f Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 25 Jul 2025 16:07:44 +0900 Subject: [PATCH 56/56] =?UTF-8?q?chore:=20=EB=B9=8C=EB=93=9C=20=EB=B2=84?= =?UTF-8?q?=EC=A0=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/Poppool.xcodeproj/project.pbxproj | 4 ++-- Poppool/Poppool/Resource/Info.plist | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index 7759d420..b854517c 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -438,7 +438,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 250715.1858; + CURRENT_PROJECT_VERSION = 250725.1554; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 9JZYRP3D46; ENABLE_USER_SCRIPT_SANDBOXING = NO; @@ -485,7 +485,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 250715.1858; + CURRENT_PROJECT_VERSION = 250725.1554; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 9JZYRP3D46; ENABLE_USER_SCRIPT_SANDBOXING = NO; diff --git a/Poppool/Poppool/Resource/Info.plist b/Poppool/Poppool/Resource/Info.plist index 98e00bfb..61793be6 100644 --- a/Poppool/Poppool/Resource/Info.plist +++ b/Poppool/Poppool/Resource/Info.plist @@ -23,7 +23,7 @@ CFBundleVersion - 250715.1858 + 250725.1554 KAKAO_AUTH_APP_KEY ${KAKAO_AUTH_APP_KEY} LSApplicationQueriesSchemes